我最近突发奇想,用timeit
测试了这两种方法,看看哪种评估方法更快:
import timeit
"""Test method returns True if either argument is falsey, else False."""
def and_chk((a, b)):
if not (a and b):
return True
return False
def not_or_chk((a, b)):
if not a or not b:
return True
return False
…结果是:
^{pr2}$效率的差异在1%到9%之间,总是有利于if not (a and b)
,这与我可能预期的相反,因为我知道if not a or not b
将按顺序计算其项(if not a
,然后if not b
),一旦遇到真正的表达式(并且没有and
子句),它将运行if
块。相反,and_chk
方法需要计算两个子句,然后才能将任何结果返回给包装它的if not..
。在
然而,时间安排的结果却否定了这种理解。那么,如何计算if
条件?我完全意识到这样的微观优化程度实际上是毫无意义的。我只想了解Python是怎么做的。在
为了完成,这是我如何设置timeit
。。。在
cyc = 1111111
bothFalse_and = iter([(0,0)] * cyc)
zeroTrue_and = iter([(1,0)] * cyc)
oneTrue_and = iter([(0,1)] * cyc)
bothTrue_and = iter([(1,1)] * cyc)
bothFalse_notor = iter([(0,0)] * cyc)
zeroTrue_notor = iter([(1,0)] * cyc)
oneTrue_notor = iter([(0,1)] * cyc)
bothTrue_notor = iter([(1,1)] * cyc)
time_bothFalse_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import bothFalse_and as tups, and_chk')
time_zeroTrue_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import zeroTrue_and as tups, and_chk')
time_oneTrue_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import oneTrue_and as tups, and_chk')
time_bothTrue_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import bothTrue_and as tups, and_chk')
time_bothFalse_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import bothFalse_notor as tups, not_or_chk')
time_zeroTrue_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import zeroTrue_notor as tups, not_or_chk')
time_oneTrue_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import oneTrue_notor as tups, not_or_chk')
time_bothTrue_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import bothTrue_notor as tups, not_or_chk')
…然后用.timeit(cyc)
运行每个timeit.Timer(..)
函数,以获得发布的结果。在
TL;DR
not_or_chk
函数需要两个一元运算来对两个跳转进行加法运算(在最坏的情况下),而and_chk
函数只有两个跳转(同样,在最坏的情况下)。在细节
救命的dis module!
dis
模块允许您查看代码的Python字节码反汇编。例如:产生以下输出:
^{pr2}$看看我用星号标记的两个Python字节码块。这两个块是你分解的两个函数。注意,
and_chk
只有两个跳转,函数中的计算是在决定是否执行跳转时进行的。在另一方面,
not_or_chk
函数要求在最坏的情况下执行两次not
操作,此外,还需要由解释器决定是否执行跳转。在我刚刚通过你的Meta-SO问题注意到了这个问题:Is it appropriate to share the results of my research toward solving my own minor questions?。正如你在那个问题中提到的(这个问题上的一个标签表明的),这类事情属于微优化的范畴。理想情况下,人们不必担心这些细微的性能差异,正如Knuth所说,premature optimization is the root of all evil。但是,我想调查这些问题是有趣和有启发性的,因为它可以让您更好地了解Python是如何“隐藏”工作的。在
Mephy's comment提示我查看您的函数的
if
版本的时间差异。结果很有趣,伊莫。我也借此机会简化了你的测试程序。在输出
^{pr2}$这些计时是在运行mepis11(Debian系列Linux发行版)的2Ghzpentium4(单核32位)上使用python2.6.6执行的。在
您会注意到,我避免了使用
next(tups)
策略来获取每个函数调用的参数,而是直接将参数作为常量传递。执行next(tups)
所花的时间应该是相当恒定的,但是最好在实际情况下消除这样的开销,以便报告的时间度量更准确地反映我们真正感兴趣的代码的性能。在此外,通常要重复几次定时循环,取最小值;FWIW,我通常重复3到5次。从timeit docs:
您的Meta post说您希望对其他微优化实验执行报告,因此您可能有兴趣看看我几个月前发布的一些代码,这些代码对factorial函数的各种实现进行时间测试。在
相关问题 更多 >
编程相关推荐