如何使用timeit模块

Nee*_*imo 323 python time timeit

我理解的是什么timeit,但我不知道如何在我的代码中实现它.

我如何比较两个功能,说insertion_sorttim_sort,用timeit

Sve*_*ach 264

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

  1. 使用IPython shell.它具有方便的%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
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在标准Python解释器中,您可以通过__main__在setup语句中导入它们来访问先前在交互式会话期间定义的函数和其他名称:

    >>> 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]
    
    Run Code Online (Sandbox Code Playgroud)

  • 用于显示``from __main__ import f``技术的+1.我不认为这应该是广为人知的.在这样的情况下,它正在定时函数或方法调用时很有用.在其他情况下(计时一系列步骤),它没那么有用,因为它引入了函数调用开销. (89认同)
  • 你可以做`%timeit f(x)` (14认同)

Ray*_*ger 257

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
Run Code Online (Sandbox Code Playgroud)

请注意,一系列语句在每次传递时都会生成未分类数据的新副本.

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

这些是我正确使用timeit的提示.希望这可以帮助 :-)

  • @max使用min()而不是时间的平均值.这是我,Tim Peters和Guido van Rossum的推荐.最快的时间表示当加载高速缓存并且系统不忙于其他任务时算法可以执行的最佳时间.所有时间都很嘈杂 - 最快的时间是噪音最小的.很容易证明最快的时序是最可重复的,因此在计时两种不同的实现时最有用. (65认同)
  • 是的,它包括列表副本(与排序本身相比非常快).如果你不复制,第一次传递对列表进行排序,剩下的传递不需要做任何工作.如果你想知道排序的时间,那么运行上面有和没有``timsort(a)``并采取区别:-) (7认同)
  • 我只能补充说你如何分配7000次执行的预算(例如,`.repeat(7,1000)`vs .repeat(2,335)`vs`.repeat(35,200`)应该取决于如何由于系统负载引起的错误与输入可变性引起的错误相比较.在极端情况下,如果您的系统总是处于高负载状态,并且您在执行时间分布的左侧看到一个很长的细尾(当您在罕见的情况下捕获它时)如果你不能预算超过7000次运行,你甚至可能会发现`.repeat(7000,1)`比`.repeat(7,1000)`更有用. (5认同)
  • 您计算1000个输入的*平均值*(好,总数,但它相当); 然后重复7次,并取*最小*.您需要平均超过1000个输入,因为您需要平均(非最佳情况)算法复杂度.你需要最低限度的原因.我想我可以通过选择一个输入来改进你的方法,运行算法7次,取最小值; 然后重复1000个不同的输入,取平均值.我没有意识到你的`.repeat(7,1000)`已经这样做了(通过使用相同的种子)!所以你的解决方案是完美的IMO. (4认同)
  • 难道显示的时间不包括在语句'a = s [:]`中复制列表的时间吗?如果你想获得纯粹的分拣时间,你会怎么做? (2认同)

Vee*_*rac 126

我会告诉你一个秘密:最好的使用方法timeit是在命令行上.

在命令行上,进行timeit适当的统计分析:它告诉你最短的运行时间.这很好,因为时间上的所有错误都是正的.因此,最短的时间内错误最少.没有办法得到负面错误,因为计算机无法计算得比计算速度快!

所以,命令行界面:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
Run Code Online (Sandbox Code Playgroud)

那很简单,是吗?

你可以设置东西:

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

这也很有用!

如果你想要多行,你可以使用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
Run Code Online (Sandbox Code Playgroud)

这给出了一个设置

x = range(1000)
y = range(100)
Run Code Online (Sandbox Code Playgroud)

和时代

sum(x)
min(y)
Run Code Online (Sandbox Code Playgroud)

如果你想拥有更长的脚本,你可能会想要进入timeitPython脚本.我建议避免这种情况,因为在命令行上分析和计时更好.相反,我倾向于制作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
Run Code Online (Sandbox Code Playgroud)

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


但是如果你timeit在你的模块中使用怎么办?

嗯,简单的方法是:

def function(...):
    ...

timeit.Timer(function).timeit(number=NUMBER)
Run Code Online (Sandbox Code Playgroud)

这会给你累计(不是最小!)时间来运行这么多次.

要获得良好的分析,请使用.repeat并采取最低限度:

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Run Code Online (Sandbox Code Playgroud)

您通常应该将其与之相结合,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
Run Code Online (Sandbox Code Playgroud)

你也可以这样做:

timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
Run Code Online (Sandbox Code Playgroud)

这会让你从命令行更接近界面,但是以一种不那么酷的方式.将"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
    
    Run Code Online (Sandbox Code Playgroud)

    嗯,它不是 0.0476μs.你只知道它不到那个.所有错误都是正面的.

    所以试着找到纯粹的开销:

    >>> python -m timeit -s "x = 0" ""      
    100000000 loops, best of 3: 0.014 usec per loop
    
    Run Code Online (Sandbox Code Playgroud)

    从时间开始,这是一个很好的30%开销!这可以大大扭曲相对时间.但你真的很关心增加时间; 查询时间x也需要包含在开销中:

    >>> python -m timeit -s "x = 0" "x"
    100000000 loops, best of 3: 0.0166 usec per loop
    
    Run Code Online (Sandbox Code Playgroud)

    差别不大,但它就在那里.

  • 变异方法很危险.

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

    但这完全错了! x是第一次迭代后的空列表.您需要重新初始化:

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

    但是你有很多开销.分别考虑到这一点.

    >>> python -m timeit "x = [0]*100000"                   
    1000 loops, best of 3: 261 usec per loop
    
    Run Code Online (Sandbox Code Playgroud)

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

    对于你的榜样,这是值得注意的是,这两个插入排序和蒂姆排序有完全不同寻常的已排序的列表时序行为.这意味着random.shuffle如果你想避免破坏你的时间,你需要在各种类型之间.

  • usec 是什么意思?是微秒吗? (2认同)
  • @HasanIqbalAnik是的. (2认同)

zza*_*art 88

如果您想快速比较两个代码/函数块,您可以:

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)
Run Code Online (Sandbox Code Playgroud)


unu*_*tbu 41

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

鉴于test.py:

def InsertionSort(): ...
def TimSort(): ...
Run Code Online (Sandbox Code Playgroud)

像这样运行timeit:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'
Run Code Online (Sandbox Code Playgroud)


Rod*_*una 14

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

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


timeit.timeit(stmt=foo, number=1234567)
Run Code Online (Sandbox Code Playgroud)


小智 11

# ????????? ????? ?????

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)
Run Code Online (Sandbox Code Playgroud)


Oha*_*bin 7

这非常有效:

  python -m timeit -c "$(cat file_name.py)"
Run Code Online (Sandbox Code Playgroud)

  • 如果脚本需要任何参数,如何传递参数? (2认同)

Séb*_*ski 7

只需将整个代码作为 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))
Run Code Online (Sandbox Code Playgroud)


str*_*sed 5

这个问题已经得到了很多回答,但我只是想说,我发现直接传递函数比传递字符串(如所有其他答案)或弄乱包装器更符合人体工程学。例如,

import timeit

def my_sort(lst: list) -> list:
    do_something()

lst = [2, 3, 1]
timeit.timeit(lambda: my_sort(lst))
Run Code Online (Sandbox Code Playgroud)

附带说明一下,“timeit.repeat”也非常有用,因为它返回可用于执行自己的分析的时间列表。还推荐perfplot,它在内部完成了大部分工作,并在最后生成了一个漂亮的图表。