timeit– 测量小代码片段的执行时间

源代码: Lib / timeit.py


这个模块提供了一种简单的方法来计算一小段Python代码。它有botha 命令行界面以及可调用一。它避免了一些测量执行时间的常见陷阱。参见Tim Peters对O’Reilly发表的PythonCookbook中“算法”一章的介绍.

基本实例

以下示例显示了命令行界面如何用于比较三个不同的表达式:

$ python3 -m timeit ""-".join(str(n) for n in range(100))"10000 loops, best of 5: 30.2 usec per loop$ python3 -m timeit ""-".join([str(n) for n in range(100)])"10000 loops, best of 5: 27.5 usec per loop$ python3 -m timeit ""-".join(map(str, range(100)))"10000 loops, best of 5: 23.2 usec per loop

这可以从 Python实现界面 with:

>>> import timeit>>> timeit.timeit(""-".join(str(n) for n in range(100))", number=10000)0.3018611848820001>>> timeit.timeit(""-".join([str(n) for n in range(100)])", number=10000)0.2727368790656328>>> timeit.timeit(""-".join(map(str, range(100)))", number=10000)0.23702679807320237

但请注意,只有在使用命令行界面时,timeit才会自动确定重复次数。在示例部分,你可以找到更高级的例子.

Python界面

模块定义了三个便捷函数和一个公共类:

timeit.timeitstmt=”pass”, setup=”pass”, timer=<default timer>, number=1000000, globals=None

创建一个Timer实例给定的陈述,setup代码和timer函数并用timeit()执行运行它的number方法。可选的globals参数指定一个命名空间,在其中执行code.

更改版本3.5:可选globals参数被添加了

timeit.repeatstmt=”pass”, setup=”pass”, timer=<default timer>, repeat=5, number=1000000, globals=None)

用给定的陈述创建Timer实例,setup代码和timer使用给定的repeat() count和repeat运行并运行number处决。可选的globals参数指定执行代码的anamespace.

在版本3.5中更改:添加了可选的globals参数

在版本3.7中更改:默认值repeat从3变为5.

timeit.default_timer

默认计时器,总是time.perf_counter().

改版3.3:time.perf_counter()现在是默认的计时器.

class timeit.Timerstmt=”pass”, setup=”pass”, timer=<timer function>, globals=None

小代码片段的定时执行速度等级.

构造函数接受一个定时语句,一个用于设置的附加语句和一个定时器函数。两个语句都默认为"pass";定时器功能与平台有关(参见模块文件字符串).stmtsetup也可能包含多个以;或换行符,只要它们不包含多行字符串文字。默认情况下,语句将在timeit的命名空间内执行;可以通过将命名空间传递给globals.

要测量第一个语句的执行时间,请使用timeit()方法。repeat()autorange()方法是方便的方法来调用timeit()多次。

执行时间setup被排除在整体定时执行运行之外

stmtsetup参数也可以采用可用参数调用的对象。这将在定时器函数中嵌入对它们的调用,然后由timeit()。请注意,由于额外的函数调用,在这种情况下,时间开销会略大一些.

在版本3.5中更改:可选globals参数被添加了

timeitnumber=1000000

时间number执行主要声明。这将执行一次setupstatement,然后返回执行main语句多次所需的时间,以秒为单位测量为float。参数是循环次数,默认为一百万。主要语句,设置语句和使用的定时器函数传递给构造函数.

注意

默认情况下,timeit()暂时关闭垃圾收集在时间安排。这种方法的优点是可以使独立时间更具可比性。该缺点是GC可能是所测量功能性能的重要组成部分。如果是这样,可以重新启用GC作为setup字符串中的第一个句子。例如:

timeit.Timer("for i in range(10): oct(i)", "gc.enable()").timeit()
autorangecallback=None

自动确定调用的次数timeit().

这是一个调用timeit()重复,总时间\u003e = 0.2秒,返回最终(循环次数,循环次数所用的时间)。它叫timeit()从序列1,2,5,10,20,50,……中增加的数字,直到所用的时间至少为0.2秒

如果给出callback而不是None,它将被称为后续审判,有两个论点:callback(number, time_taken).

新版本3.6.

repeat (repeat=5, number=1000000)

Call timeit() a几次

这是一个方便的功能,叫timeit()反复,返回结果列表。第一个参数指定调用timeit()的次数。第二个参数指定number timeit().

的参数注意

很容易从结果向量计算平均值和标准偏差并报告这些偏差。但是,这不是很有用。在典型情况下,最低值给出了机器运行给定代码片段的速度的下限;结果向量中较高的值通常不是由Python的速度变化引起的,而是由于其他过程干扰了你的计时精度。所以结果的min()可能是你应该感兴趣的唯一数字。之后,您应该查看整个向量并应用常识而不是统计.

更改版本3.7: repeat的默认值从3更改为5.

print_excfile=None

帮助从定时代码打印回溯.

典型使用:

t = Timer(...)       # outside the try/excepttry:    t.timeit(...)    # or t.repeat(...)except Exception:    t.print_exc()

优于标准回溯的优点是来源将显示编译模板中的行。可选的file参数指示发送回溯的地方;它默认为sys.stderr.

命令行界面

当从命令行调用程序时,使用以下形式:

python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]

其中可以理解以下选项:

-n N, --number=N

执行’statement’多少次

-r N, --repeat=N

多少次重复计时器(默认5)

-s S, --setup=S

语句最初执行一次(默认pass

-p, --process

测量处理时间,而不是挂钟时间,使用time.process_time()代替 time.perf_counter(),这是默认的

3.3版本中的新功能

-u, --unit=U
指定定时器输出的时间单位;可以选择nsec,usec,msec或sec

版本3.5中的新功能

-v, --verbose

打印原始计时结果;重复更多数字精确度

-h, --help

打印一条简短的用法信息并退出

可以通过将每一行指定为单独的语句参数来给出多行语句;通过在引用中引用参数并使用前导空格,可以缩进行。多 -s选项的处理方式相同.

如果没有给出-n,则通过试验连续10次计算合适的循环数,直到总时间至少为0.2秒.

default_timer()测量可能受到在同一台机器上运行的其他程序的影响,因此在需要精确计时时最好的做法是重复几次计时并使用最佳时间。-r选择对此有利;在大多数情况下,默认的5次重复可能就足够了。您可以使用time.process_time()来测量CPU时间.

注意

执行pass语句会产生一定的基线开销。此处的代码不会尝试隐藏它,但您应该注意它。可以通过不带参数调用程序来测量baseline开销,并且它可能在Python版本之间有所不同.

示例

可以提供一个只在开头执行一次的setup语句:

$ python -m timeit -s "text = "sample string"; char = "g""  "char in text"5000000 loops, best of 5: 0.0877 usec per loop$ python -m timeit -s "text = "sample string"; char = "g""  "text.find(char)"1000000 loops, best of 5: 0.342 usec per loop
>>> import timeit>>> timeit.timeit("char in text", setup="text = "sample string"; char = "g"")0.41440500499993504>>> timeit.timeit("text.find(char)", setup="text = "sample string"; char = "g"")1.7246671520006203

使用Timer类及其方法可以完成同样的操作:

>>> import timeit>>> t = timeit.Timer("char in text", setup="text = "sample string"; char = "g"")>>> t.timeit()0.3955516149999312>>> t.repeat()[0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]

以下示例显示如何计算包含多行的表达式。这里我们比较使用hasattr()try/except的成本来测试缺少的和当前的对象属性:

$ python -m timeit "try:" "  str.__bool__" "except AttributeError:" "  pass"20000 loops, best of 5: 15.7 usec per loop$ python -m timeit "if hasattr(str, "__bool__"): pass"50000 loops, best of 5: 4.26 usec per loop$ python -m timeit "try:" "  int.__bool__" "except AttributeError:" "  pass"200000 loops, best of 5: 1.43 usec per loop$ python -m timeit "if hasattr(int, "__bool__"): pass"100000 loops, best of 5: 2.23 usec per loop
>>> import timeit>>> # attribute is missing>>> s = """\... try:...     str.__bool__... except AttributeError:...     pass... """>>> timeit.timeit(stmt=s, number=100000)0.9138244460009446>>> s = "if hasattr(str, "__bool__"): pass">>> timeit.timeit(stmt=s, number=100000)0.5829014980008651>>>>>> # attribute is present>>> s = """\... try:...     int.__bool__... except AttributeError:...     pass... """>>> timeit.timeit(stmt=s, number=100000)0.04215312199994514>>> s = "if hasattr(int, "__bool__"): pass">>> timeit.timeit(stmt=s, number=100000)0.08588060699912603

要给timeit模块访问你定义的函数,你可以传递setup参数,其中包含一个import语句:

def test():    """Stupid test function"""    L = [i for i in range(100)]if __name__ == "__main__":    import timeit    print(timeit.timeit("test()", setup="from __main__ import test"))

另一个选项是传递globals()globals参数,这将导致代码在您当前的全局命名空间中执行。这比单独指定import更方便:

def f(x):    return x**2def g(x):    return x**4def h(x):    return x**8import timeitprint(timeit.timeit("[func(42) for func in (f,g,h)]", globals=globals()))