Python 程序运行时间测试

849 阅读3分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」。

python的标准库有个叫做timeit的库。它可以被用来测试代码的运行速度。

如果我们使用 Ipython 这种交互式的 python 解释器,那么可以通过一个简单的操作测试代码的运行速度。 具体操作像下面这样(这里假设测试以下 numpy 数组自带的排序效率):

%%timeit

np.random.randint(1000,size=1000).sort()

运行这段代码之后,下方的输出会显示类似下面的字样,指示通过多次运行得到大致运行时间

38.2 µs ± 2.11 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

但是一个显然的事实是它只能做到一个简单的运行时间显示。可操作性不强。 为了更好地做一些测试,我们可以尝试使用timeit模块对外提供的模块。

首先,与我们在ipython中使用的类似,timeit提供了同名的函数timeit用于测试代码运行速率。我们只需要为它传入一段可执行的代码,它就会返回一个多次运行求出的平均运行时间。

timeit("[0]",number=100) 

这里指定number语句的原因是默认的运行次数太多(1000000次),在简单语句的测试时还让人能够接受,但当我们操作的数据过多,运行时间稍微长一点,这就是让人不可接受的了。因此number被设定成了100。

在这种字符串式的代码片段中,它是不支持外部定义的类和函数之类的东西的。应该说这样认为,这个字符串代码可以当作一个单独的作用域。如果直接使用自定义的函数,那么将会出现无法name not found的错误。 当然我们可以用三引号文本块,然后写import语句。


def foo():
    [0]

timeit("""
from  __main__ import foo
foo()
""", number=100)

只是每次测试都额外做导入也不太好。这属于额外开销了,timeit也考虑到了这一点,因此它额外有一个setup的参数,这样可以把导包的语句写到setup参数中

timeit("""

foo()
""", number=100,setup="from  __main__ import foo")

这样就避免了额外开销。

除此之外,timeit还提供了另一种更好用的测试方式,通过传入一个函数做测试。 像下面这样

timeit(foo, number=100)

不过这个函数不能传参数,它应该作为一个无参函数被调用。对应简单的测试代码片段,这是没什么问题,但是如果我们需要做对比的话,就需要使用python中的闭包了。毕竟对比讲究的是控制变量。 比如说我们测试python的list和numpy数组的排序速度。

def test_function_gen(length: int):
    x = np.random.randint(0, 10000, size=1000 * (length + 1))

    def test_list():
        arr = x.tolist()
        arr.sort()

    def test_numpy():
        arr = x.copy()
        arr.sort()

    return test_list, test_numpy


def benchmark(func):
    duration = timeit.Timer(func).timeit(number=num_runs)
    return duration / num_runs

这里用闭包生成测试函数,之后准备交由benchmark测试时间 我们测试下1000到10000的排序时间


result = []
for epoch in range(10):
    num_runs = 20
    python_list_test, numpy_array_test = test_function_gen(epoch)
    t1 = benchmark(python_list_test)
    t2 = benchmark(numpy_array_test)
    result.append((t1, t2))


list_times, numpy_times = np.array(result).T
label_x = np.arange(1000, 10001, 1000)
plt.plot(label_x, list_times, '-b', label="python list")
plt.plot(label_x, numpy_times, '--r', label="numpy ndarray")

plt.legend()
plt.show()

顺便用matplotlib画下示意图

image.png 到这里我们就完成程序的运行时间测试了。