Python 如何使用timeit模块

Python 如何使用timeit模块,python,time,timeit,Python,Time,Timeit,我理解timeit的概念,但我不确定如何在我的代码中实现它 如何将两个函数(例如insertion\u sort和tim\u sort)与timeit进行比较?我发现使用timeit最简单的方法是从命令行: 给定测试.py: def InsertionSort(): ... def TimSort(): ... 运行时间如下所示: % python -mtimeit -s'import test' 'test.InsertionSort()' % python -mtimeit -s'impo

我理解
timeit
的概念,但我不确定如何在我的代码中实现它


如何将两个函数(例如
insertion\u sort
tim\u sort
)与
timeit
进行比较?

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

给定测试.py

def InsertionSort(): ...
def TimSort(): ...
运行时间如下所示:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.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
请注意,该系列语句在每次传递时都会生成未排序数据的新副本

另外,请注意七次运行度量套件并只保留最佳时间的计时技术——这确实有助于减少系统上运行的其他进程导致的度量失真


这些是我正确使用timeit的技巧。希望这有帮助:-)

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

  • 使用外壳。它具有方便的
    %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
    
  • 在标准Python解释器中,您可以通过从setup语句中的
    \uuuuu main\uuuu
    导入函数和之前在交互式会话中定义的其他名称来访问它们:

    >>> 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)
    
    这会给你累计(而不是最短!)的时间来运行这个次数

    要获得良好的分析,请使用
    。重复
    ,并取最小值:

    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)
    
    这将使您从命令行中获得更接近界面的东西,但方式要冷淡得多。
    “从主导入…”
    允许您在由
    timeit
    创建的人工环境中使用主模块中的代码

    值得注意的是,这是一个方便的
    Timer(…).timeit(…)
    包装器,因此并不特别擅长计时。我个人更喜欢使用
    计时器(…)。重复(…)
    ,如上所示


    警告
    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
      
      >>> python -m timeit -s "x = 0" "x"
      100000000 loops, best of 3: 0.0166 usec per loop
      
      这是一个很好的30%开销,仅从时间上看!这会严重扭曲相对计时。但你真正关心的只是增加时间;
      x
      的查找时间也需要包含在开销中:

      >>> python -m timeit -s "x = 0" ""      
      100000000 loops, best of 3: 0.014 usec per loop
      
      >>> 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

    当您运行这个程序时,您可以看到索引比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

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

    # Генерация целых чисел
    
    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)
    
    要从模块内为功能计时,请执行以下操作:

    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)
    
    您将创建两个funct