timeit偶尔返回负值,可能是bug
我在做Project Euler的问题时,决定加个计时功能,于是用timeit
来测量main()
的运行时间,代码如下(为了方便,我在main()
里存了个全局变量RESULT
)
t = timeit.timeit(main, 'gc.enable()', number=1)
print("# Euler", PROBLEM, ".py RESULT: ", RESULT))
这样做效果不错。但是,有些运行得太快了,我想试试别的方法。
t = timeit.timeit(main, 'gc.enable()', number=1)
if (t < 0.001):
t2 = timeit.timeit(main, 'gc.enable()', number=1000)
有时候这样做是有效的。不过如果我反复运行,有时会得到负值的t2。以Euler #2为例,我连续运行5次得到的结果是这样的。
# Euler2.py RESULT: 4613732 3.17869758716347e-05 seconds
repeats timing -3.7966224778973e-05 sec per call
# Euler2.py RESULT: 4613732 3.1558464885145785e-05 seconds
repeats timing 2.4836235955056177e-05 sec per call
# Euler2.py RESULT: 4613732 3.131149340411519e-05 seconds
repeats timing -3.5684903805855466e-05 sec per call
# Euler2.py RESULT: 4613732 3.177450256451194e-05 seconds
repeats timing 2.4558941864410162e-05 sec per call
# Euler2.py RESULT: 4613732 3.158939868681022e-05 seconds
repeats timing 2.4268726425449536e-05 sec per call
现在如果我把重复次数改成100,000次或更多,就完全看不到负的t2值了,每次调用的时间稳定在大约2.4e-5秒。
如果我重复10,000次,情况又有些不同。t2值始终是正的,但数值波动很大。10次运行的结果是
repeats timing 2.4194581894745244e-05 sec per call
repeats timing 1.8200670315524775e-05 sec per call
repeats timing 2.4408832248987168e-05 sec per call
repeats timing 2.4378118077314547e-05 sec per call
repeats timing 1.8361976570139902e-05 sec per call
repeats timing 1.8055080028738498e-05 sec per call
repeats timing 1.8102133534236732e-05 sec per call
repeats timing 2.4485323058654477e-05 sec per call
repeats timing 3.118363087991698e-05 sec per call
repeats timing 1.803846408685413e-05 sec per call
最后,我把重复次数设为1000,并去掉了最开始的(repeat=1)timeit。结果和之前差不多,有些负值,波动也很大。
我用Python 2.7重复了这个过程,结果类似——其他的都是3.4版本。
在我看来,这看起来像是timeit功能的一个bug,因为总时间和系统定时器的中断时间差不多,但我觉得可能是我漏掉了什么。
补充一下
我还知道其他的计时函数,比如perf_counter()。
我特别问timeit()是因为我觉得它是个简单易用的高精度计时器,可以在新旧版本的Python中使用,如果它是可靠的,我希望继续使用。
补充一下
根据得到的回答,我把我的计时代码改成了基于perf_counter(),结果发现有时也会得到负值。所以,如果在旧的Windows系统上使用,小的计时增量根本不可靠。这就是我想知道的。让我觉得问题出在Python的原因是,对于非常小的计时,数值似乎是准确的。应该猜到是Windows和设备驱动的组合问题。
1 个回答
在Windows系统上,timeit默认使用time.clock
来获取时间,而这个函数又是通过Windows的APIQueryPerformanceCounter
来工作的。根据Windows的版本和机器的性能,QueryPerformanceCounter
会使用处理器的时间戳计数器(TSC)作为计时器。不过,一些老旧的多处理器机器在不同处理器之间无法保持TSC的同步,导致它们向Windows报告的时间不准确,或者在尝试同步时出现了错误。这就导致了QueryPerformanceCounter
返回的结果在不同处理器上执行时,看起来像是跳来跳去的。
微软在MSDN上对这个问题有详细的描述:http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
AMD为Windows XP系统发布了一个修复这个问题的工具,叫做AMD Dual Core Optimizer。