简体   繁体   English

如何使用timeit模块

[英]How to use timeit module

I understand the concept of what timeit does but I am not sure how to implement it in my code.我理解timeit做什么的概念,但我不确定如何在我的代码中实现它。

How can I compare two functions, say insertion_sort and tim_sort , with timeit ?我如何比较两个功能,说insertion_sorttim_sort ,与timeit

If you want to use timeit in an interactive Python session, there are two convenient options:如果要在交互式 Python 会话中使用timeit ,有两个方便的选项:

  1. Use the IPython shell.使用IPython外壳。 It features the convenient %timeit special function:它具有方便的%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:在标准 Python 解释器中,您可以通过在 setup 语句中从__main__导入它们来访问您之前在交互式会话期间定义的函数和其他名称:

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

The way timeit works is to run setup code once and then make repeated calls to a series of statements.timeit 的工作方式是运行一次设置代码,然后重复调用一系列语句。 So, if you want to test sorting, some care is required so that one pass at an in-place sort doesn't affect the next pass with already sorted data (that, of course, would make the Timsort really shine because it performs best when the data already partially ordered).因此,如果您想测试排序,则需要小心谨慎,这样就地排序的一次传递不会影响已排序数据的下一次传递(当然,这会使Timsort真正发光,因为它表现最佳当数据已经部分排序时)。

Here is an example of how to set up a test for sorting:以下是如何设置排序测试的示例:

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

Note that the series of statements makes a fresh copy of the unsorted data on every pass.请注意,这一系列语句在每次通过时都会生成未排序数据的新副本。

Also, note the timing technique of running the measurement suite seven times and keeping only the best time -- this can really help reduce measurement distortions due to other processes running on your system.另外,请注意运行测量套件七次并只保留最佳时间的计时技术——这确实有助于减少由于系统上运行的其他进程而导致的测量失真。

Those are my tips for using timeit correctly.这些是我正确使用 timeit 的技巧。 Hope this helps :-)希望这可以帮助 :-)

I'll let you in on a secret: the best way to use timeit is on the command line.我会告诉你一个秘密:使用timeit的最佳方式是在命令行上。

On the command line, timeit does proper statistical analysis: it tells you how long the shortest run took.在命令行上, timeit会进行适当的统计分析:它会告诉您最短运行需要多长时间。 This is good because all error in timing is positive.这很好,因为所有计时错误都是正的。 So the shortest time has the least error in it.所以最短的时间误差最小。 There's no way to get negative error because a computer can't ever compute faster than it can compute!没有办法得到负错误,因为计算机的计算速度永远不会超过它的计算速度!

So, the command-line interface:所以,命令行界面:

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

That's quite simple, eh?这很简单吧?

You can set stuff up:你可以设置:

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

which is useful, too!这也很有用!

If you want multiple lines, you can either use the shell's automatic continuation or use separate arguments:如果需要多行,可以使用 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

That gives a setup of这给出了一个设置

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

and times和次数

sum(x)
min(y)

If you want to have longer scripts you might be tempted to move to timeit inside a Python script.如果您想要更长的脚本,您可能会倾向于在 Python 脚本中使用timeit I suggest avoiding that because the analysis and timing is simply better on the command line.我建议避免这种情况,因为命令行上的分析和计时更好。 Instead, I tend to make shell scripts:相反,我倾向于制作 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

This can take a bit longer due to the multiple initialisations, but normally that's not a big deal.由于多次初始化,这可能需要更长的时间,但通常这没什么大不了的。


But what if you want to use timeit inside your module?但是如果你在你的模块中使用timeit怎么办?

Well, the simple way is to do:那么,简单的方法是这样做:

def function(...):
    ...

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

and that gives you cumulative ( not minimum!) time to run that number of times.这为您提供了累积(不是最小!)时间来运行该次数。

To get a good analysis, use .repeat and take the minimum:要获得良好的分析,请使用.repeat并取最小值:

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

You should normally combine this with functools.partial instead of lambda: ... to lower overhead.您通常应该将其与functools.partial而不是lambda: ...结合使用以降低开销。 Thus you could have something like:因此,你可以有类似的东西:

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

You can also do:你也可以这样做:

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

which would give you something closer to the interface from the command-line, but in a much less cool manner.这将使您从命令行获得更接近界面的东西,但以一种不那么酷的方式。 The "from __main__ import ..." lets you use code from your main module inside the artificial environment created by timeit . "from __main__ import ..."允许您在timeit创建的人工环境中使用主模块中的代码。

It's worth noting that this is a convenience wrapper for Timer(...).timeit(...) and so isn't particularly good at timing.值得注意的是,这是Timer(...).timeit(...)的便利包装器,因此不是特别擅长计时。 I personally far prefer using Timer(...).repeat(...) as I've shown above.我个人更喜欢使用Timer(...).repeat(...)如我上面所示。


Warnings警告

There are a few caveats with timeit that hold everywhere. timeit有一些警告在任何地方都适用。

  • Overhead is not accounted for.不考虑开销。 Say you want to time x += 1 , to find out how long addition takes:假设您想计算x += 1时间,以了解加法需要多长时间:

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

    Well, it's not 0.0476 µs.好吧,它不是0.0476 µs。 You only know that it's less than that.你只知道它比那 All error is positive.所有的错误都是积极的。

    So try and find pure overhead:所以试着找出纯粹的开销:

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

    That's a good 30% overhead just from timing!这是一个很好的30% 的开销! This can massively skew relative timings.这可能会严重扭曲相对时间。 But you only really cared about the adding timings;但你只关心添加时间; the look-up timings for x also need to be included in overhead: x的查找时间也需要包含在开销中:

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

    The difference isn't much larger, but it's there.差异并不大,但确实存在。

  • Mutating methods are dangerous.变异方法是危险的。

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

    But that's completely wrong!但这是完全错误的! x is the empty list after the first iteration. x是第一次迭代后的空列表。 You'll need to reinitialize:您需要重新初始化:

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

    But then you have lots of overhead.但是你有很多开销。 Account for that separately.单独考虑。

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

    Note that subtracting the overhead is reasonable here only because the overhead is a small-ish fraction of the time.请注意,这里减去开销是合理的,因为开销只是时间的一小部分。

    For your example, it's worth noting that both Insertion Sort and Tim Sort have completely unusual timing behaviours for already-sorted lists.对于你的榜样,值得一提的,无论插入排序和蒂姆排序对已排序的列表完全不同寻常的时刻的行为。 This means you will require a random.shuffle between sorts if you want to avoid wrecking your timings.这意味着如果你想避免破坏你的时间,你将需要在排序之间进行random.shuffle

If you want to compare two blocks of code / functions quickly you could do:如果你想快速比较两个代码块/函数,你可以这样做:

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)

I find the easiest way to use timeit is from the command line:我发现使用 timeit 的最简单方法是从命令行:

Given test.py :鉴于test.py

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

run timeit like this:像这样运行时间:

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

for me, this is the fastest way:对我来说,这是最快的方法:

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


timeit.timeit(stmt=foo, number=1234567)
# Генерация целых чисел

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)

这很好用:

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

simply pass your entire code as an argument of timeit:只需将整个代码作为 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))

lets setup the same dictionary in each of the following and test the execution time.让我们在下面的每一个中设置相同的字典并测试执行时间。

The setup argument is basically setting up the dictionary setup 参数基本上是设置字典

Number is to run the code 1000000 times.次数是运行代码1000000次。 Not the setup but the stmt不是设置而是stmt

When you run this you can see that index is way faster than get.当您运行它时,您可以看到 index 比 get 快得多。 You can run it multiple times to see.您可以多次运行它来查看。

The code basically tries to get the value of c in the dictionary.代码基本上尝试获取字典中 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))

Here are my results, yours will differ.这是我的结果,你的会有所不同。

by index: 0.20900007452246427按索引:0.20900007452246427

by get: 0.54841166886888通过获取:0.54841166886888

The built-in timeit module works best from the IPython command line.内置的 timeit 模块在 IPython 命令行中效果最佳。

To time functions from within a module:从模块内计时函数:

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

Example of how to use Python REPL interpreter with function that accepts parameters.如何将 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                                                                                        
import timeit


def oct(x):
   return x*x


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

You would create two functions and then run something similar to this.您将创建两个函数,然后运行与此类似的操作。 Notice, you want to choose the same number of execution/run to compare apple to apple.请注意,您希望选择相同数量的执行/运行来比较苹果与苹果。
This was tested under Python 3.7.这是在 Python 3.7 下测试的。

在此处输入图片说明 Here is the code for ease of copying it这是便于复制的代码

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM