“Python/docs/3.9/library/timeit”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:timeit — 测量小代码片段的执行时间 — Python 文档}} | ||
<div id="module-timeit" class="section"> | <div id="module-timeit" class="section"> | ||
<span id="timeit-measure-execution-time-of-small-code-snippets"></span> | <span id="timeit-measure-execution-time-of-small-code-snippets"></span> | ||
− | = | + | = timeit — 测量小代码片段的执行时间 = |
− | ''' | + | '''源代码:''' [[#id1|<span id="id2" class="problematic">:source:`Lib/timeit.py`</span>]] |
− | + | ||
− | + | ----- | |
− | + | ||
− | + | 该模块提供了一种简单的方法来计时一小段 Python 代码。 它既有 [[#timeit-command-line-interface|命令行接口]] ,也有 [[#python-interface|可调用]] 接口。 它避免了许多用于测量执行时间的常见陷阱。 另请参阅 O'Reilly 出版的 ''Python Cookbook'' 第二版中 Tim Peters 对“算法”章节的介绍。 | |
− | Cookbook'' | ||
<div id="basic-examples" class="section"> | <div id="basic-examples" class="section"> | ||
− | == | + | == 基本示例 == |
− | + | 以下示例显示了如何使用 [[#timeit-command-line-interface|命令行界面]] 来比较三种不同的表达式: | |
− | |||
<div class="highlight-shell-session notranslate"> | <div class="highlight-shell-session notranslate"> | ||
第23行: | 第22行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | <pre>$ python3 -m timeit '"-".join(str(n) for n in range(100))' | + | <pre class="session">$ python3 -m timeit '"-".join(str(n) for n in range(100))' |
10000 loops, best of 5: 30.2 usec per loop | 10000 loops, best of 5: 30.2 usec per loop | ||
$ python3 -m timeit '"-".join([str(n) for n in range(100)])' | $ python3 -m timeit '"-".join([str(n) for n in range(100)])' | ||
第33行: | 第32行: | ||
</div> | </div> | ||
− | + | 这可以通过 [[#python-interface|Python 接口]] 实现: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第39行: | 第38行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> import timeit |
− | + | >>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000) | |
0.3018611848820001 | 0.3018611848820001 | ||
− | + | >>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000) | |
0.2727368790656328 | 0.2727368790656328 | ||
− | + | >>> timeit.timeit('"-".join(map(str, range(100)))', number=10000) | |
− | 0.23702679807320237</ | + | 0.23702679807320237</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 也可以从 [[#python-interface|Python 接口]] 传递一个可调用对象: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第56行: | 第55行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000) |
− | 0.19665591977536678</ | + | 0.19665591977536678</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是请注意,只有在使用命令行界面时, [[#timeit.timeit|timeit()]] 才会自动确定重复次数。 在 [[#timeit-examples|示例]] 部分,您可以找到更高级的示例。 | |
− | |||
− | [[#timeit-examples| | ||
第70行: | 第67行: | ||
<div id="python-interface" class="section"> | <div id="python-interface" class="section"> | ||
− | <span id=" | + | <span id="id3"></span> |
− | == | + | == Python接口 == |
− | + | 该模块定义了三个便利函数和一个公共类: | |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">timeit.</span></span><span class="sig-name descname"><span class="pre">timeit</span></span><span class="sig-paren">(</span>''<span class="pre">stmt='pass'</span>'', ''<span class="pre">setup='pass'</span>'', ''<span class="pre">timer=<default</span> <span class="pre">timer></span>'', ''<span class="pre">number=1000000</span>'', ''<span class="pre">globals=None</span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>使用给定的语句、''setup'' 代码和 ''timer'' 函数创建一个 [[#timeit.Timer|Timer]] 实例,并使用 ''编号运行其 [[#timeit.Timer.timeit|timeit()]] 方法'' 处决。 可选的 ''globals'' 参数指定执行代码的命名空间。</p> |
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed">在 3.5 | + | <p><span class="versionmodified changed"> 在 3.5 版更改: </span> 添加了可选的 ''globals'' 参数。</p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">timeit.</span></span><span class="sig-name descname"><span class="pre">repeat</span></span><span class="sig-paren">(</span>''<span class="pre">stmt='pass'</span>'', ''<span class="pre">setup='pass'</span>'', ''<span class="pre">timer=<default</span> <span class="pre">timer></span>'', ''<span class="pre">repeat=5</span>'', ''<span class="pre">number=1000000</span>'', ''<span class="pre">globals=None</span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>使用给定的语句、''setup'' 代码和 ''timer'' 函数创建一个 [[#timeit.Timer|Timer]] 实例,并使用给定的 ''运行其 [[#timeit.Timer.repeat|repeat()]] 方法]repeat'' 计数和 ''number'' 次执行。 可选的 ''globals'' 参数指定执行代码的命名空间。</p> |
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed">在 3.5 | + | <p><span class="versionmodified changed"> 在 3.5 版更改: </span> 添加了可选的 ''globals'' 参数。</p> |
</div> | </div> | ||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed"> | + | <p><span class="versionmodified changed"> 3.7 版本变更: </span> ''repeat'' 的默认值从 3 更改为 5。</p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">timeit.</span></span><span class="sig-name descname"><span class="pre">default_timer</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>默认计时器,始终为 [[../time#time|time.perf_counter()]]。</p> |
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed">在 3.3 | + | <p><span class="versionmodified changed"> 在 3.3 版更改: </span>[[../time#time|time.perf_counter()]] 现在是默认计时器。</p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">timeit.</span></span><span class="sig-name descname"><span class="pre">Timer</span></span><span class="sig-paren">(</span>''<span class="pre">stmt='pass'</span>'', ''<span class="pre">setup='pass'</span>'', ''<span class="pre">timer=<timer</span> <span class="pre">function></span>'', ''<span class="pre">globals=None</span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>用于计时小代码片段执行速度的类。</p> |
− | <p> | + | <p>构造函数接受一个要计时的语句、一个用于设置的附加语句和一个计时器函数。 两个语句都默认为 <code>'pass'</code>; 计时器功能是平台相关的(请参阅模块文档字符串)。 ''stmt'' 和 ''setup'' 也可以包含由 <code>;</code> 或换行符分隔的多个语句,只要它们不包含多行字符串文字。 该语句将默认在 timeit 的命名空间内执行; 这种行为可以通过将命名空间传递给 ''globals'' 来控制。</p> |
− | + | <p>要测量第一条语句的执行时间,请使用 [[#timeit.Timer.timeit|timeit()]] 方法。 [[#timeit.Timer.repeat|repeat()]] 和 [[#timeit.Timer.autorange|autorange()]] 方法是多次调用 [[#timeit.Timer.timeit|timeit()]] 的便捷方法。</p> | |
− | + | <p>''setup'' 的执行时间不包括在整体定时执行运行中。</p> | |
− | ''stmt'' | + | <p>''stmt'' 和 ''setup'' 参数也可以采用无需参数即可调用的对象。 这会将对它们的调用嵌入到一个定时器函数中,然后由 [[#timeit.Timer.timeit|timeit()]] 执行。 请注意,在这种情况下,由于额外的函数调用,计时开销会稍大一些。</p> |
− | |||
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
− | <p> | ||
− | <p> | ||
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed">在 3.5 | + | <p><span class="versionmodified changed"> 在 3.5 版更改: </span> 添加了可选的 ''globals'' 参数。</p> |
</div> | </div> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">timeit</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">number</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1000000</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>主语句的执行时间为 ''number'' 次。 这将执行一次 setup 语句,然后返回多次执行主语句所需的时间,以秒为单位作为浮点数。 参数是循环的次数,默认为一百万。 main 语句、setup 语句和要使用的定时器函数被传递给构造函数。</p> |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
− | <p> | + | <p>笔记</p> |
− | <p> | + | <p>默认情况下,[[#timeit.Timer.timeit|timeit()]]在计时期间暂时关闭[[../../glossary#term-garbage-collection|垃圾收集]]。 这种方法的优点是它使独立时序更具可比性。 缺点是 GC 可能是被测量函数性能的重要组成部分。 如果是这样,GC 可以作为 ''setup'' 字符串中的第一条语句重新启用。 例如:</p> |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()</syntaxhighlight> |
</div> | </div> | ||
第166行: | 第136行: | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">autorange</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>自动确定调用 [[#timeit.Timer.timeit|timeit()]] 的次数。</p> |
− | <p> | + | <p>这是一个方便的函数,它重复调用 [[#timeit.Timer.timeit|timeit()]] 使总时间 >= 0.2 秒,返回最终结果(循环次数,该循环次数所用的时间)。 它调用 [[#timeit.Timer.timeit|timeit()]] 从序列 1, 2, 5, 10, 20, 50, ... 递增的数字,直到花费的时间至少为 0.2 秒。</p> |
− | + | <p>如果给出 ''callback'' 并且不是 <code>None</code>,它将在每次试验后用两个参数调用:<code>callback(number, time_taken)</code>。</p> | |
− | |||
− | [[#timeit.Timer.timeit| | ||
− | 10, 20, 50, ... | ||
− | <p> | ||
− | |||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.6 | + | <p><span class="versionmodified added">3.6 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">repeat</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">repeat</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">5</span></span>'', ''<span class="n"><span class="pre">number</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1000000</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>多次调用 [[#timeit.Timer.timeit|timeit()]]。</p> |
− | <p> | + | <p>这是一个方便的函数,它重复调用 [[#timeit.Timer.timeit|timeit()]],返回结果列表。 第一个参数指定调用 [[#timeit.Timer.timeit|timeit()]] 的次数。 第二个参数指定 [[#timeit.Timer.timeit|timeit()]] 的 ''number'' 参数。</p> |
− | |||
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
− | <p> | + | <p>笔记</p> |
− | <p> | + | <p>从结果向量计算均值和标准差并报告这些是很诱人的。 但是,这不是很有用。 在典型情况下,最低值给出了机器运行给定代码片段的速度的下限; 结果向量中的较高值通常不是由 Python 速度的变化引起的,而是由干扰计时精度的其他进程引起的。 因此,结果的 [[../functions#min|min()]] 可能是您应该感兴趣的唯一数字。 之后,您应该查看整个向量并应用常识而不是统计数据。</p> |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
</div> | </div> | ||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed"> | + | <p><span class="versionmodified changed"> 3.7 版本变更: </span> ''repeat'' 的默认值从 3 更改为 5。</p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">print_exc</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">file</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>帮助从定时代码打印回溯。</p> |
− | <p> | + | <p>典型用途:</p> |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">t = Timer(...) # outside the try/except |
try: | try: | ||
t.timeit(...) # or t.repeat(...) | t.timeit(...) # or t.repeat(...) | ||
except Exception: | except Exception: | ||
− | t.print_exc()</ | + | t.print_exc()</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>与标准回溯相比的优点是将显示编译模板中的源代码行。 可选的 ''file'' 参数指示发送回溯的位置; 它默认为 [[../sys#sys|sys.stderr]]。</p></dd></dl> |
− | |||
− | |||
</dd></dl> | </dd></dl> | ||
第235行: | 第187行: | ||
<span id="timeit-command-line-interface"></span> | <span id="timeit-command-line-interface"></span> | ||
− | == | + | == 命令行界面 == |
− | + | 当从命令行作为程序调用时,使用以下形式: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第243行: | 第195行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 理解以下选项的地方: | |
− | ; <span id="cmdoption-timeit-number"></span>< | + | ; <span id="cmdoption-timeit-number"></span><span class="sig-name descname"><span class="pre">-n</span></span><span class="sig-prename descclassname"> <span class="pre">N</span></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--number</span></span><span class="sig-prename descclassname"><span class="pre">=N</span></span> |
− | : | + | : 执行“语句”多少次 |
− | ; <span id="cmdoption-timeit-repeat"></span>< | + | ; <span id="cmdoption-timeit-repeat"></span><span class="sig-name descname"><span class="pre">-r</span></span><span class="sig-prename descclassname"> <span class="pre">N</span></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--repeat</span></span><span class="sig-prename descclassname"><span class="pre">=N</span></span> |
− | : | + | : 重复计时器多少次(默认 5) |
− | ; <span id="cmdoption-timeit-setup"></span>< | + | ; <span id="cmdoption-timeit-setup"></span><span class="sig-name descname"><span class="pre">-s</span></span><span class="sig-prename descclassname"> <span class="pre">S</span></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--setup</span></span><span class="sig-prename descclassname"><span class="pre">=S</span></span> |
− | : | + | : 最初执行一次的语句(默认 <code>pass</code>) |
<dl> | <dl> | ||
− | <dt><span id="cmdoption-timeit-process"></span>< | + | <dt><span id="cmdoption-timeit-process"></span><span class="sig-name descname"><span class="pre">-p</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--process</span></span><span class="sig-prename descclassname"></span></dt> |
− | <dd><p> | + | <dd><p>使用 [[../time#time|time.process_time()]] 代替默认的 [[../time#time|time.perf_counter()]] 测量进程时间,而不是挂钟时间</p> |
− | |||
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.3 | + | <p><span class="versionmodified added">3.3 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt><span id="cmdoption-timeit-unit"></span>< | + | <dt><span id="cmdoption-timeit-unit"></span><span class="sig-name descname"><span class="pre">-u</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--unit</span></span><span class="sig-prename descclassname"><span class="pre">=U</span></span></dt> |
− | <dd><p> | + | <dd><p>指定定时器输出的时间单位; 可以选择 nsec、usec、msec 或 sec</p> |
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.5 | + | <p><span class="versionmodified added">3.5 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
− | ; <span id="cmdoption-timeit-verbose"></span>< | + | ; <span id="cmdoption-timeit-verbose"></span><span class="sig-name descname"><span class="pre">-v</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--verbose</span></span><span class="sig-prename descclassname"></span> |
− | : | + | : 打印原始计时结果; 重复以获得更多数字精度 |
− | ; <span id="cmdoption-timeit-help"></span>< | + | ; <span id="cmdoption-timeit-help"></span><span class="sig-name descname"><span class="pre">-h</span></span><span class="sig-prename descclassname"></span><span class="sig-prename descclassname"><span class="pre">,</span> </span><span class="sig-name descname"><span class="pre">--help</span></span><span class="sig-prename descclassname"></span> |
− | : | + | : 打印一个简短的使用信息并退出 |
− | + | 可以通过将每一行指定为单独的语句参数来给出多行语句; 通过将参数括在引号中并使用前导空格,可以使用缩进行。 多个 [[#cmdoption-timeit-s|-s]] 选项的处理方式类似。 | |
− | |||
− | |||
− | |||
− | + | 如果未给出 [[#cmdoption-timeit-n|-n]],则通过尝试从序列 1、2、5、10、20、50 中增加数字来计算合适的循环次数,直到总时间至少为 0.2 秒。 | |
− | |||
− | |||
− | [[#timeit.default_timer| | + | [[#timeit.default_timer|default_timer()]] 测量值可能会受到同一台机器上运行的其他程序的影响,因此在需要精确计时时最好的做法是重复计时几次并使用最佳时间。 [[#cmdoption-timeit-r|-r]] 选项对此很有用; 在大多数情况下,5 次重复的默认值可能就足够了。 您可以使用 [[../time#time|time.process_time()]] 来测量 CPU 时间。 |
− | |||
− | |||
− | |||
− | |||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 执行 pass 语句有一定的基线开销。 此处的代码不会试图隐藏它,但您应该意识到这一点。 可以通过不带参数调用程序来测量基线开销,并且它可能因 Python 版本而异。 | |
− | |||
− | |||
− | |||
第315行: | 第254行: | ||
<span id="timeit-examples"></span> | <span id="timeit-examples"></span> | ||
− | == | + | == 例子 == |
− | + | 可以提供一个在开始时只执行一次的 setup 语句: | |
<div class="highlight-shell-session notranslate"> | <div class="highlight-shell-session notranslate"> | ||
第323行: | 第262行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | <pre>$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text' | + | <pre class="session">$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text' |
5000000 loops, best of 5: 0.0877 usec per loop | 5000000 loops, best of 5: 0.0877 usec per loop | ||
$ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)' | $ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)' | ||
第335行: | 第274行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> import timeit |
− | + | >>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"') | |
0.41440500499993504 | 0.41440500499993504 | ||
− | + | >>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"') | |
− | 1.7246671520006203</ | + | 1.7246671520006203</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 使用 [[#timeit.Timer|Timer]] 类及其方法也可以做到这一点: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第350行: | 第289行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> import timeit |
− | + | >>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"') | |
− | + | >>> t.timeit() | |
0.3955516149999312 | 0.3955516149999312 | ||
− | + | >>> t.repeat() | |
− | [0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]</ | + | [0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 以下示例显示如何对包含多行的表达式计时。 在这里,我们比较了使用 [[../functions#hasattr|hasattr()]] 与使用的成本。 [[../../reference/compound_stmts#try|try]]/[[../../reference/compound_stmts#except|except]] 测试缺少和存在的对象属性: | |
− | |||
− | |||
<div class="highlight-shell-session notranslate"> | <div class="highlight-shell-session notranslate"> | ||
第368行: | 第305行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | <pre>$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass' | + | <pre class="session">$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass' |
20000 loops, best of 5: 15.7 usec per loop | 20000 loops, best of 5: 15.7 usec per loop | ||
$ python -m timeit 'if hasattr(str, "__bool__"): pass' | $ python -m timeit 'if hasattr(str, "__bool__"): pass' | ||
第385行: | 第322行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> import timeit |
− | + | >>> # attribute is missing | |
− | + | >>> s = """\ | |
... try: | ... try: | ||
... str.__bool__ | ... str.__bool__ | ||
... except AttributeError: | ... except AttributeError: | ||
... pass | ... pass | ||
− | ... | + | ... """ |
− | + | >>> timeit.timeit(stmt=s, number=100000) | |
0.9138244460009446 | 0.9138244460009446 | ||
− | + | >>> s = "if hasattr(str, '__bool__'): pass" | |
− | + | >>> timeit.timeit(stmt=s, number=100000) | |
0.5829014980008651 | 0.5829014980008651 | ||
− | + | >>> | |
− | + | >>> # attribute is present | |
− | + | >>> s = """\ | |
... try: | ... try: | ||
... int.__bool__ | ... int.__bool__ | ||
... except AttributeError: | ... except AttributeError: | ||
... pass | ... pass | ||
− | ... | + | ... """ |
− | + | >>> timeit.timeit(stmt=s, number=100000) | |
0.04215312199994514 | 0.04215312199994514 | ||
− | + | >>> s = "if hasattr(int, '__bool__'): pass" | |
− | + | >>> timeit.timeit(stmt=s, number=100000) | |
− | 0.08588060699912603</ | + | 0.08588060699912603</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 要让 [[#module-timeit|timeit]] 模块访问您定义的函数,您可以传递包含导入语句的 ''setup'' 参数: | |
− | ''setup'' | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第422行: | 第358行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">def test(): |
− | + | """Stupid test function""" | |
L = [i for i in range(100)] | L = [i for i in range(100)] | ||
if __name__ == '__main__': | if __name__ == '__main__': | ||
import timeit | import timeit | ||
− | print(timeit.timeit( | + | print(timeit.timeit("test()", setup="from __main__ import test"))</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 另一种选择是将 [[../functions#globals|globals()]] 传递给 ''globals'' 参数,这将导致代码在您当前的全局命名空间中执行。 这比单独指定导入更方便: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第441行: | 第375行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">def f(x): |
return x**2 | return x**2 | ||
def g(x): | def g(x): | ||
第449行: | 第383行: | ||
import timeit | import timeit | ||
− | print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))</ | + | print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))</syntaxhighlight> |
+ | |||
+ | </div> | ||
</div> | </div> | ||
第456行: | 第392行: | ||
</div> | </div> | ||
+ | <div class="clearer"> | ||
+ | |||
+ | |||
</div> | </div> | ||
− | [[Category:Python 3.9 | + | [[Category:Python 3.9 文档]] |
2021年10月31日 (日) 04:53的最新版本
timeit — 测量小代码片段的执行时间
该模块提供了一种简单的方法来计时一小段 Python 代码。 它既有 命令行接口 ,也有 可调用 接口。 它避免了许多用于测量执行时间的常见陷阱。 另请参阅 O'Reilly 出版的 Python Cookbook 第二版中 Tim Peters 对“算法”章节的介绍。
基本示例
以下示例显示了如何使用 命令行界面 来比较三种不同的表达式:
$ 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 接口 实现:
也可以从 Python 接口 传递一个可调用对象:
但是请注意,只有在使用命令行界面时, timeit() 才会自动确定重复次数。 在 示例 部分,您可以找到更高级的示例。
Python接口
该模块定义了三个便利函数和一个公共类:
- timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
使用给定的语句、setup 代码和 timer 函数创建一个 Timer 实例,并使用 编号运行其 timeit() 方法 处决。 可选的 globals 参数指定执行代码的命名空间。
在 3.5 版更改: 添加了可选的 globals 参数。
- timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)
使用给定的语句、setup 代码和 timer 函数创建一个 Timer 实例,并使用给定的 运行其 repeat() 方法]repeat 计数和 number 次执行。 可选的 globals 参数指定执行代码的命名空间。
在 3.5 版更改: 添加了可选的 globals 参数。
3.7 版本变更: repeat 的默认值从 3 更改为 5。
- timeit.default_timer()
默认计时器,始终为 time.perf_counter()。
在 3.3 版更改: time.perf_counter() 现在是默认计时器。
- class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)
用于计时小代码片段执行速度的类。
构造函数接受一个要计时的语句、一个用于设置的附加语句和一个计时器函数。 两个语句都默认为
'pass'
; 计时器功能是平台相关的(请参阅模块文档字符串)。 stmt 和 setup 也可以包含由;
或换行符分隔的多个语句,只要它们不包含多行字符串文字。 该语句将默认在 timeit 的命名空间内执行; 这种行为可以通过将命名空间传递给 globals 来控制。要测量第一条语句的执行时间,请使用 timeit() 方法。 repeat() 和 autorange() 方法是多次调用 timeit() 的便捷方法。
setup 的执行时间不包括在整体定时执行运行中。
stmt 和 setup 参数也可以采用无需参数即可调用的对象。 这会将对它们的调用嵌入到一个定时器函数中,然后由 timeit() 执行。 请注意,在这种情况下,由于额外的函数调用,计时开销会稍大一些。
在 3.5 版更改: 添加了可选的 globals 参数。
- timeit(number=1000000)
主语句的执行时间为 number 次。 这将执行一次 setup 语句,然后返回多次执行主语句所需的时间,以秒为单位作为浮点数。 参数是循环的次数,默认为一百万。 main 语句、setup 语句和要使用的定时器函数被传递给构造函数。
- autorange(callback=None)
自动确定调用 timeit() 的次数。
这是一个方便的函数,它重复调用 timeit() 使总时间 >= 0.2 秒,返回最终结果(循环次数,该循环次数所用的时间)。 它调用 timeit() 从序列 1, 2, 5, 10, 20, 50, ... 递增的数字,直到花费的时间至少为 0.2 秒。
如果给出 callback 并且不是
None
,它将在每次试验后用两个参数调用:callback(number, time_taken)
。3.6 版中的新功能。
- repeat(repeat=5, number=1000000)
多次调用 timeit()。
这是一个方便的函数,它重复调用 timeit(),返回结果列表。 第一个参数指定调用 timeit() 的次数。 第二个参数指定 timeit() 的 number 参数。
笔记
从结果向量计算均值和标准差并报告这些是很诱人的。 但是,这不是很有用。 在典型情况下,最低值给出了机器运行给定代码片段的速度的下限; 结果向量中的较高值通常不是由 Python 速度的变化引起的,而是由干扰计时精度的其他进程引起的。 因此,结果的 min() 可能是您应该感兴趣的唯一数字。 之后,您应该查看整个向量并应用常识而不是统计数据。
3.7 版本变更: repeat 的默认值从 3 更改为 5。
- print_exc(file=None)
帮助从定时代码打印回溯。
典型用途:
与标准回溯相比的优点是将显示编译模板中的源代码行。 可选的 file 参数指示发送回溯的位置; 它默认为 sys.stderr。
命令行界面
当从命令行作为程序调用时,使用以下形式:
理解以下选项的地方:
- -n N, --number=N
- 执行“语句”多少次
- -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,则通过尝试从序列 1、2、5、10、20、50 中增加数字来计算合适的循环次数,直到总时间至少为 0.2 秒。
default_timer() 测量值可能会受到同一台机器上运行的其他程序的影响,因此在需要精确计时时最好的做法是重复计时几次并使用最佳时间。 -r 选项对此很有用; 在大多数情况下,5 次重复的默认值可能就足够了。 您可以使用 time.process_time() 来测量 CPU 时间。
笔记
执行 pass 语句有一定的基线开销。 此处的代码不会试图隐藏它,但您应该意识到这一点。 可以通过不带参数调用程序来测量基线开销,并且它可能因 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
使用 Timer 类及其方法也可以做到这一点:
以下示例显示如何对包含多行的表达式计时。 在这里,我们比较了使用 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
要让 timeit 模块访问您定义的函数,您可以传递包含导入语句的 setup 参数:
另一种选择是将 globals() 传递给 globals 参数,这将导致代码在您当前的全局命名空间中执行。 这比单独指定导入更方便: