考虑以下功能:
def fact1(n):
if n < 2:
return 1
else:
return n * fact1(n-1)
def fact2(n):
if n < 2:
return 1
return n * fact2(n-1)
它们应该是等价的。但是存在性能差异:
>>> T(lambda : fact1(1)).repeat(number=10000000)
[2.5754408836364746, 2.5710129737854004, 2.5678811073303223]
>>> T(lambda : fact2(1)).repeat(number=10000000)
[2.8432059288024902, 2.834425926208496, 2.8364310264587402]
没有else
的版本慢了10%。这非常重要。为什么呢?
答案 0 :(得分:12)
对我来说,它们的速度几乎相同:( Debian上的Python 2.6.6)
In [4]: %timeit fact1(1)
10000000 loops, best of 3: 151 ns per loop
In [5]: %timeit fact2(1)
10000000 loops, best of 3: 154 ns per loop
字节代码也非常相似:
In [6]: dis.dis(fact1)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (2)
6 COMPARE_OP 0 (<)
9 JUMP_IF_FALSE 5 (to 17)
12 POP_TOP
3 13 LOAD_CONST 2 (1)
16 RETURN_VALUE
>> 17 POP_TOP
5 18 LOAD_FAST 0 (n)
21 LOAD_GLOBAL 0 (fact)
24 LOAD_FAST 0 (n)
27 LOAD_CONST 2 (1)
30 BINARY_SUBTRACT
31 CALL_FUNCTION 1
34 BINARY_MULTIPLY
35 RETURN_VALUE
36 LOAD_CONST 0 (None)
39 RETURN_VALUE
In [7]: dis.dis(fact2)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (2)
6 COMPARE_OP 0 (<)
9 JUMP_IF_FALSE 5 (to 17)
12 POP_TOP
3 13 LOAD_CONST 2 (1)
16 RETURN_VALUE
>> 17 POP_TOP
4 18 LOAD_FAST 0 (n)
21 LOAD_GLOBAL 0 (fact)
24 LOAD_FAST 0 (n)
27 LOAD_CONST 2 (1)
30 BINARY_SUBTRACT
31 CALL_FUNCTION 1
34 BINARY_MULTIPLY
35 RETURN_VALUE
唯一的区别是带有else
的版本包含在控件到达函数体末尾时返回None
的代码。
答案 1 :(得分:12)
这里发生的事情是fact2
与模块全局变量中的__name__
存在哈希冲突。这使得全局fact2
的查找速度变得稍慢。
>>> [(k, hash(k) % 32) for k in globals().keys() ]
[('__builtins__', 8), ('__package__', 15), ('fact2', 25), ('__name__', 25), ('fact1', 26), ('__doc__', 29)]
即。答案与Why is early return slower than else?相同,只是哈希冲突与__builtins__
答案 2 :(得分:9)
我质疑时间。这两个函数没有递归到它们自己。 fact1 和 fact2 都调用 fact ,但未显示。
一旦修复,反汇编(在Py2.6和Py2.7中)显示两者都运行相同的操作码,除了递归到函数的名称。名称的选择会触发时间上的小差异,因为 fact1 可能会在模块字典中插入而没有名称冲突,而* fact2)可能有一个哈希值与模块中的另一个名称冲突。
换句话说,您在时间上看到的任何差异都不是由于选择是否存在else子句: - )