How to use timeit module
我理解
如何将
如果要在交互式Python会话中使用
使用IPython shell。它具有方便的
1 2 3 4 5 6 | In [1]: def f(x): ...: return x*x ...: In [2]: %timeit for x in range(100): f(x) 100000 loops, best of 3: 20.3 us per loop |
在标准的Python解释器中,您可以通过在setup语句中从
1 2 3 4 5 6 7 | >>> def f(x): ... return x * x ... >>> import timeit >>> timeit.repeat("for x in range(100): f(x)","from __main__ import f", number=100000) [2.0640320777893066, 2.0876040458679199, 2.0520210266113281] |
timeit的工作方式是运行一次安装代码,然后重复调用一系列语句。所以,如果你想测试排序,需要注意一点,这样就地排序的一次传递不会影响已经排序数据的下一次传递(当然,这会让Timsort真正发光,因为它表现最好当数据已经部分订购时)。
以下是如何设置排序测试的示例:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> import timeit >>> setup = ''' import random random.seed('slartibartfast') s = [random.random() for i in range(1000)] timsort = list.sort ''' >>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000)) 0.334147930145 |
请注意,系列语句在每次传递时都会生成未分类数据的新副本。
另外,请注意运行测量套件七次并保持最佳时间的计时技术 - 这可以真正帮助减少由于系统上运行的其他进程而导致的测量失真。
这些是我正确使用timeit的提示。希望这可以帮助 :-)
我将告诉你一个秘密:使用
在命令行中,
所以,命令行界面:
1 2 | %~> python -m timeit"1 + 2" 10000000 loops, best of 3: 0.0468 usec per loop |
那很简单,是吗?
你可以设置东西:
1 2 | %~> python -m timeit -s"x = range(10000)""sum(x)" 1000 loops, best of 3: 543 usec per loop |
这也很有用!
如果你想要多行,你可以使用shell的自动延续或使用单独的参数:
1 2 | %~> python -m timeit -s"x = range(10000)" -s"y = range(100)""sum(x)""min(y)" 1000 loops, best of 3: 554 usec per loop |
这给出了一个设置
1 2 | x = range(1000) y = range(100) |
和时代
1 2 | sum(x) min(y) |
如果您想要更长的脚本,可能会想要在Python脚本中移动到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | SETUP=" ... # lots of stuff " echo Minmod arr1 python -m timeit -s"$SETUP""Minmod(arr1)" echo pure_minmod arr1 python -m timeit -s"$SETUP""pure_minmod(arr1)" echo better_minmod arr1 python -m timeit -s"$SETUP""better_minmod(arr1)" ... etc |
由于多次初始化,这可能需要更长的时间,但通常这不是什么大问题。
但是如果你想在你的模块中使用
嗯,简单的方法是:
1 2 3 4 | def function(...): ... timeit.Timer(function).timeit(number=NUMBER) |
这会给你累计(不是最小!)时间来运行这么多次。
要获得良好的分析,请使用
1 | min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER)) |
您通常应将此与
1 2 3 4 5 6 7 8 9 10 | from functools import partial def to_time(items): ... test_items = [1, 2, 3] * 100 times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000) # Divide by the number of repeats time_taken = min(times) / 1000 |
你也可以这样做:
1 | timeit.timeit("...", setup="from __main__ import ...", number=NUMBER) |
这会让你从命令行更接近界面,但是以一种不那么酷的方式。
值得注意的是,这是
警告
-
不计入间接费用。假设您想要时间
x += 1 ,以了解添加需要多长时间:1
2>>> python -m timeit -s"x = 0""x += 1"
10000000 loops, best of 3: 0.0476 usec per loop嗯,它不是0.0476μs。你只知道它不到那个。所有错误都是正面的。
所以试着找到纯粹的开销:
1
2>>> python -m timeit -s"x = 0"""
100000000 loops, best of 3: 0.014 usec per loop从时间开始,这是一个很好的30%开销!这可以大大扭曲相对时间。但你真的很关心增加时间;
x 的查找时序也需要包含在开销中:1
2>>> python -m timeit -s"x = 0""x"
100000000 loops, best of 3: 0.0166 usec per loop差别不大,但它就在那里。
-
变异方法很危险。
1
2>>> python -m timeit -s"x = [0]*100000""while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop但这完全错了!
x 是第一次迭代后的空列表。您需要重新初始化:1
2>>> python -m timeit"x = [0]*100000""while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop但是你有很多开销。分别考虑到这一点。
1
2>>> python -m timeit"x = [0]*100000"
1000 loops, best of 3: 261 usec per loop注意,减去开销在这里是合理的,因为开销是时间的一小部分。
对于您的示例,值得注意的是,Insertion Sort和Tim Sort对已经排序的列表具有完全不同寻常的计时行为。这意味着如果你想避免破坏你的时间,你需要在
random.shuffle 之间进行排序。
如果您想快速比较两个代码/函数块,您可以:
1 2 3 4 5 6 7 8 9 | import timeit start_time = timeit.default_timer() func1() print(timeit.default_timer() - start_time) start_time = timeit.default_timer() func2() print(timeit.default_timer() - start_time) |
我发现使用timeit的最简单方法是从命令行:
鉴于test.py:
1 2 | def InsertionSort(): ... def TimSort(): ... |
像这样运行timeit:
1 2 | % python -mtimeit -s'import test' 'test.InsertionSort()' % python -mtimeit -s'import test' 'test.TimSort()' |
对我来说,这是最快的方式:
1 2 3 4 5 6 | import timeit def foo(): print("here is my code to time...") timeit.timeit(stmt=foo, number=1234567) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # Генерация целых чисел def gen_prime(x): multiples = [] results = [] for i in range(2, x+1): if i not in multiples: results.append(i) for j in range(i*i, x+1, i): multiples.append(j) return results import timeit # Засекаем время start_time = timeit.default_timer() gen_prime(3000) print(timeit.default_timer() - start_time) # start_time = timeit.default_timer() # gen_prime(1001) # print(timeit.default_timer() - start_time) |
这非常有效:
1 | python -m timeit -c"$(cat file_name.py)" |
让我们在下面的每一个中设置相同的字典并测试执行时间。
setup参数基本上是设置字典
Number是运行代码1000000次。不是设置而是stmt
运行此操作时,您可以看到索引比get快。你可以多次运行它来查看。
代码基本上试图在字典中获取c的值。
1 2 3 4 | import timeit print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000)) print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000)) |
以下是我的结果,您的结果会有所不同。
按索引:0.20900007452246427
获取:0.54841166886888
只需将整个代码作为timeit的参数传递:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import timeit print(timeit.timeit(""" limit = 10000 prime_list = [i for i in range(2, limit+1)] for prime in prime_list: for elem in range(prime*2, max(prime_list)+1, prime): if elem in prime_list: prime_list.remove(elem)""" , number=10)) |
1 2 3 4 5 6 7 8 | import timeit def oct(x): return x*x timeit.Timer("for x in range(100): oct(x)","gc.enable()").timeit() |
您将创建两个函数,然后运行类似于此的东西。
注意,你想选择相同数量的执行/运行来比较苹果和苹果。
这是在Python 3.7下测试的。
这是便于复制它的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | !/usr/local/bin/python3 import timeit def fibonacci(n): """ Returns the n-th Fibonacci number. """ if(n == 0): result = 0 elif(n == 1): result = 1 else: result = fibonacci(n-1) + fibonacci(n-2) return result if __name__ == '__main__': import timeit t1 = timeit.Timer("fibonacci(13)","from __main__ import fibonacci") print("fibonacci ran:",t1.timeit(number=1000),"milliseconds") |
如何将Python REPL解释器与接受参数的函数一起使用的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | >>> import timeit >>> def naive_func(x): ... a = 0 ... for i in range(a): ... a += i ... return a >>> def wrapper(func, *args, **kwargs): ... def wrapper(): ... return func(*args, **kwargs) ... return wrapper >>> wrapped = wrapper(naive_func, 1_000) >>> timeit.timeit(wrapped, number=1_000_000) 0.4458435332577161 |
内置的timeit模块最适合从IPython命令行运行。
要在模块内计时功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from timeit import default_timer as timer import sys def timefunc(func, *args, **kwargs): """Time a function. args: iterations=3 Usage example: timeit(myfunc, 1, b=2) """ try: iterations = kwargs.pop('iterations') except KeyError: iterations = 3 elapsed = sys.maxsize for _ in range(iterations): start = timer() result = func(*args, **kwargs) elapsed = min(timer() - start, elapsed) print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed))) return result |