如何使用 timeit 模块

2024-12-19 09:23:00
admin
原创
79
摘要:问题描述:我该如何使用来比较我自己的函数(例如“ ”和“ ”)timeit的性能?insertion_sort`tim_sort`解决方案 1:如果您想timeit在交互式 Python 会话中使用,有两个方便的选项:使用IPython shell。它具有以下方便的%timeit特殊功能:In [1]: de...

问题描述:

我该如何使用来比较我自己的函数(例如“ ”和“ ”)timeit的性能?insertion_sort`tim_sort`


解决方案 1:

如果您想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
  1. __main__在标准 Python 解释器中,您可以通过在 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]

解决方案 2:

工作方式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.04485079200821929

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

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

解决方案 3:

我会告诉你一个秘密:最好的使用方式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)

如果您想要更长的脚本,您可能会倾向于转到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

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


但是如果您timeit在模块内部使用该怎么办?

嗯,简单的方法是:

def function(...):
    ...

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

这会为您提供运行该次数的累计时间(而非最小时间!)。

为了获得良好的分析,使用.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

请注意,这里减去开销是合理的,只是因为开销只占时间的一小部分。

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

解决方案 4:

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

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)

解决方案 5:

我发现使用 timeit 最简单的方法是通过命令行。

给定test.py

def insertion_sort(): ...
def timsort(): ...

timeit像这样运行:

% python -m timeit -s 'import test' 'test.insertion_sort()'
% python -m timeit -s 'import test' 'test.timsort()'

解决方案 6:

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

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


timeit.timeit(stmt=foo, number=1234567)

解决方案 7:

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

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)

解决方案 8:

这很有效:

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

解决方案 9:

只需将整个代码作为 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))

解决方案 10:

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

import timeit

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

lst = [2, 3, 1]
timeit.timeit(lambda: my_sort(lst))

顺便提一下,“timeit.repeat” 也非常有用,因为它会返回一个时间列表,您可以使用它来执行自己的分析。另外,还会推荐perfplot,它在内部完成大部分工作,并在最后生成一个漂亮的图表。

解决方案 11:

让我们在下面每个词典中设置相同的词典并测试执行时间。

设置参数基本上是设置字典

Number 是运行代码 1000000 次。不是 setup,而是 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

获取:0.54841166886888

解决方案 12:

import timeit


def oct(x):
   return x*x


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

解决方案 13:

内置的 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

解决方案 14:

如何使用带有接受参数的函数的 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                                                                                        

解决方案 15:

您将创建两个函数,然后运行类似这样的操作。请注意,您需要选择相同的执行/运行次数来比较同类函数。

这是在 Python 3.7 下测试的。

在此处输入图片描述
这是方便复制的代码

!/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")
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   984  
  在项目管理领域,CDCP(Certified Data Center Professional)认证评审是一个至关重要的环节,它不仅验证了项目团队的专业能力,还直接关系到项目的成功与否。在这一评审过程中,沟通技巧的运用至关重要。有效的沟通不仅能够确保信息的准确传递,还能增强团队协作,提升评审效率。本文将深入探讨CDCP...
华为IPD流程   0  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨部门协同的产品开发模式,旨在通过高效的资源整合和流程优化,提升产品开发的成功率和市场竞争力。在IPD培训课程中,掌握关键成功因素是确保团队能够有效实施这一模式的核心。以下将从五个关键成功因素展开讨论,帮助企业和...
IPD项目流程图   0  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司在其全球化进程中逐步构建和完善的一套高效产品开发管理体系。这一流程不仅帮助华为在技术创新和产品交付上实现了质的飞跃,还为其在全球市场中赢得了显著的竞争优势。IPD的核心在于通过跨部门协作、阶段性评审和市场需求驱动,确保...
华为IPD   0  
  华为作为全球领先的通信技术解决方案提供商,其成功的背后离不开一套成熟的管理体系——集成产品开发(IPD)。IPD不仅是一种产品开发流程,更是一种系统化的管理思想,它通过跨职能团队的协作、阶段评审机制和市场需求驱动的开发模式,帮助华为在全球市场中脱颖而出。从最初的国内市场到如今的全球化布局,华为的IPD体系在多个领域展现...
IPD管理流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用