如何使用timeit模块

我如何使用timeit来比较我自己的函数的性能,例如"insertion_sort"和“tim_sort" ?

399988 次浏览

我发现使用timeit最简单的方法是从命令行:

鉴于test.py:

def InsertionSort(): ...
def TimSort(): ...

像这样运行timeit:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'

timeit的工作方式是运行一次设置代码,然后重复调用一系列语句。因此,如果你想测试排序,需要一些注意,以便在原地排序时的一次传递不会影响已经排序的数据的下一次传递(当然,这会使Timsort真正发光,因为它在数据已经部分排序时表现最好)。

下面是一个如何设置排序测试的例子:

>>> 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

请注意,这一系列语句在每次传递时都对未排序的数据进行新拷贝。

另外,请注意运行测量套件7次并只保留最佳时间的计时技术——这可以真正帮助减少由于系统上运行的其他进程而导致的测量失真。

以上就是我对如何正确利用时间的建议。希望这对你有所帮助:-)

如果你想在交互式Python会话中使用timeit,有两个方便的选项:

  1. 使用IPython外壳。它具有方便的%timeit特殊函数:

    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
    
  2. In a standard Python interpreter, you can access functions and other names you defined earlier during the interactive session by importing them from __main__ in the setup statement:

    >>> 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的最好方法是在命令行上。

在命令行上,timeit进行了适当的统计分析:它告诉你最短的运行花费了多长时间。这很好,因为所有计时错误是正的。所以最短的时间误差最小。没有办法得到负错误,因为计算机的计算速度永远不会超过它的计算速度!

那么,命令行界面:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop

这很简单,是吧?

你可以设置:

%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop

这也很有用!

如果你想要多行,你可以使用shell的自动延续或者使用单独的参数:

%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop

这给出了一个

x = range(1000)
y = range(100)

和时间

sum(x)
min(y)

如果你想要更长的脚本,你可能会倾向于移动到Python脚本中的timeit。我建议避免这样做,因为在命令行上进行分析和计时会更好。相反,我倾向于编写shell脚本:

 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

由于多次初始化,这可能需要更长的时间,但通常这不是一个大问题。


但是如果你想要在你的模块中使用timeit呢?

那么,最简单的方法就是:

def function(...):
...


timeit.Timer(function).timeit(number=NUMBER)

并且这给了你累计( minimum!)时间来运行该次数。

为了得到一个好的分析,使用.repeat并取最小值:

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

你通常应该结合functools.partial而不是lambda: ...来降低开销。因此,你可以有这样的东西:

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

你还可以:

timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

这将使你更接近命令行中的接口,但以一种不那么酷的方式。"from __main__ import ..."允许你在由timeit创建的人工环境中使用主模块中的代码。

值得注意的是,这是Timer(...).timeit(...)的一个方便包装器,因此在计时方面不是特别好。我个人更喜欢使用Timer(...).repeat(...),如上所示。


警告

timeit有几个处处适用的注意事项。

  • 开销未被计入。假设你想计时x += 1,以找出加法需要多长时间:

    >>> python -m timeit -s "x = 0" "x += 1"
    10000000 loops, best of 3: 0.0476 usec per loop
    

    嗯,它是 0.0476µs。你只知道它是。所有的误差都是正的。

    因此,尝试在开销中找到:

    >>> python -m timeit -s "x = 0" ""
    100000000 loops, best of 3: 0.014 usec per loop
    

    从计时来看,这是一个很好的30%开销!这可能会极大地扭曲相对时间。但是你只关心添加计时;x的查找时间也需要包含在开销中:

    >>> python -m timeit -s "x = 0" "x"
    100000000 loops, best of 3: 0.0166 usec per loop
    

    差异不是很大,但它是存在的

  • 突变方法是危险的。

    >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
    10000000 loops, best of 3: 0.0436 usec per loop
    

    x是第一次迭代后的空列表。你需要重新初始化:

    >>> python -m timeit "x = [0]*100000" "while x: x.pop()"
    100 loops, best of 3: 9.79 msec per loop
    

    但这样你就会有很多开销。单独考虑。

    >>> python -m timeit "x = [0]*100000"
    1000 loops, best of 3: 261 usec per loop
    

    注意,在这里减去开销是合理的,开销是时间的一小部分。

    对于你的例子,值得注意的是,这两个插入排序和Tim排序对于已经排序的列表具有完全不寻常计时行为。这意味着如果你想避免破坏你的计时,你将需要在排序之间使用random.shuffle

如果你想快速比较两个代码/函数块,你可以这样做:

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)

让我们在以下每个语句中设置相同的字典并测试执行时间。

setup参数基本上是设置字典

编号是运行代码1000000次。不是设置,而是stmt

当你运行这个时,你会发现index比get快得多。您可以多次运行它来查看。

这段代码基本上是试图从字典中获取c的值。

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

get: 0.54841166886888

# Генерация целых чисел


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)

内置的timeit模块在IPython命令行中工作得最好。

为模块内的函数计时:

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

如何使用带有接受参数的函数的Python REPL解释器的示例。

>>> 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

这很有效:

  python -m timeit -c "$(cat file_name.py)"

简单地传递你的整个代码作为timeit参数:

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))

对我来说,这是最快的方法:

import timeit
def foo():
print("here is my code to time...")




timeit.timeit(stmt=foo, number=1234567)
您将创建两个函数,然后运行类似于此的程序。 注意,你想要选择相同的执行/运行数来比较apple和apple 这是在Python 3.7下测试的

enter image description here 下面是便于复制的代码

!/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")
import timeit




def oct(x):
return x*x




timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()