<p>我将告诉您一个秘密:使用<code>timeit</code>的最佳方法是在命令行上。</p>
<p>在命令行上,<code>timeit</code>进行正确的统计分析:它告诉您最短的运行时间。这很好,因为<em>所有</em>计时错误都是正的。所以最短的时间误差最小。没有办法得到负误差,因为计算机永远不能计算得比它能计算得快!</p>
<p>因此,命令行界面:</p>
<pre><code>%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
</code></pre>
<p>很简单,嗯?</p>
<p>你可以设置:</p>
<pre><code>%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
</code></pre>
<p>这也很有用!</p>
<p>如果需要多行,可以使用shell的自动延续或使用单独的参数:</p>
<pre><code>%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop
</code></pre>
<p>这样就有了</p>
<pre><code>x = range(1000)
y = range(100)
</code></pre>
<p>和时代</p>
<pre><code>sum(x)
min(y)
</code></pre>
<hr/>
<p>如果您想拥有更长的脚本,您可能会尝试在Python脚本中移动到<code>timeit</code>。我建议避免这样做,因为命令行的分析和计时更简单。相反,我倾向于制作shell脚本:</p>
<pre><code> 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
</code></pre>
<p>由于多次初始化,这可能需要更长的时间,但通常这不是什么大事。</p>
<hr/>
<p>但是如果你想在你的模块中使用<code>timeit</code>呢?</p>
<p>好吧,简单的方法是:</p>
<pre><code>def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
</code></pre>
<p>这给了你累加(<em>不是</em>最小值!)是时候跑这么多次了。</p>
<p>要获得良好的分析,请使用<code>.repeat</code>,并取最小值:</p>
<pre><code>min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
</code></pre>
<p>通常,您应该将它与<code>functools.partial</code>而不是<code>lambda: ...</code>结合使用,以降低开销。这样你就可以得到这样的东西:</p>
<pre><code>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
</code></pre>
<hr/>
<p>您还可以执行以下操作:</p>
<pre><code>timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
</code></pre>
<p>这将使您更接近命令行中的<em>接口</em>,但方式不那么酷。<code>"from __main__ import ..."</code>允许您在由<code>timeit</code>创建的人工环境中使用来自主模块的代码。</p>
<p>值得注意的是,这是一个方便的<code>Timer(...).timeit(...)</code>包装器,因此不太擅长计时。我个人更喜欢使用<code>Timer(...).repeat(...)</code>,如上面所示。</p>
<hr/>
<h3>警告</h3>
<p>有一些关于<code>timeit</code>的警告在任何地方都适用。</p>
<ul>
<li><p>不计入间接费用。假设您想计时<code>x += 1</code>,以了解加法需要多长时间:</p>
<pre><code>>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
</code></pre>
<p>好吧,它是<em>而不是</em>0.0476微秒。你只知道它<em>比这个小。所有的错误都是肯定的。</p>
<p>所以试着找出<em>纯</em>开销:</p>
<pre><code>>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
</code></pre>
<p>这是一个很好的<strong>30%</strong>从时间上来说的开销!这会严重影响相对计时。但是您只关心<em>添加</em>计时;还需要将<code>x</code>的查找计时包括在开销中:</p>
<pre><code>>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
</code></pre>
<p>差别并不大,但确实存在。</p></li>
<li><p>变异方法是危险的。</p>
<pre><code>>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
</code></pre>
<p>但那是完全错误的!</em><code>x</code>是第一次迭代后的空列表。您需要重新初始化:</p>
<pre><code>>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
</code></pre>
<p>但是你有很多开销。单独说明。</p>
<pre><code>>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
</code></pre>
<p>注意,这里减去开销是合理的,因为</em>开销只是时间的一小部分。</p>
<p>对于您的示例,值得注意的是,<em>插入排序和Tim排序对于已经排序的列表都具有完全不寻常的计时行为。这意味着您需要在排序之间使用<code>random.shuffle</code>,以避免破坏您的计时。</p></li>
</ul>