Python 为什么删除else会降低我的代码速度?
考虑以下功能:Python 为什么删除else会降低我的代码速度?,python,performance,recursion,Python,Performance,Recursion,考虑以下功能: 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) 没有else的版本会慢10%。这是相当重要的。为什么?对我来说,它们的速度几乎相同:(Debian上的Python 2.6.6) 字节码也非常相似: In
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)
没有
else
的版本会慢10%。这是相当重要的。为什么?对我来说,它们的速度几乎相同:(Debian上的Python 2.6.6)
字节码也非常相似:
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
[6]中的:dis.dis(fact1)
2 0快速加载0(n)
3负载常数1(2)
6比较顶部0(>17个顶部
5 18快速加载0(n)
21加载_全局0(事实)
24负载快速0(n)
27荷载常数2(1)
30二进制减法
31调用函数1
34二进制乘法
35返回值
36负载常数0(无)
39返回值
在[7]中:dis.dis(fact2)
2 0快速加载0(n)
3负载常数1(2)
6比较顶部0(>17个顶部
4 18快速加载0(n)
21加载_全局0(事实)
24负载快速0(n)
27荷载常数2(1)
30二进制减法
31调用函数1
34二进制乘法
35返回值
唯一的区别是,带有
else
的版本包含返回None
的代码,以防控件到达函数体的末尾。我质疑计时。这两个函数不会自己递归。fact1和fact2都调用未显示的事实
修复后,反汇编(在Py2.6和Py2.7中)显示,除递归到函数的名称外,它们都运行相同的操作码。名称的选择会触发时间上的微小差异,因为fact1可能插入模块字典,而*fact2)时没有名称冲突可能具有与模块中的其他名称冲突的哈希值
换句话说,您在计时中看到的任何差异都不是由于选择是否存在else子句所致:-)这里发生的情况是,
fact2
与模块全局中的\uu name\uuuu
存在哈希冲突。这使得全局fact2
的查找稍微慢了一点
>>> [(k, hash(k) % 32) for k in globals().keys() ]
[('__builtins__', 8), ('__package__', 15), ('fact2', 25), ('__name__', 25), ('fact1', 26), ('__doc__', 29)]
i、 e.答案与for相同,只是在您进行了多少次测试后,哈希冲突与
\uuuuu内置\uuuuuuuu有关?@M4tt4n erm<代码>。重复(数字=10000000)
@Cat Plus是的,但是找出事情为什么以这种方式运行是很有趣的,对吗?@GabiPurcaru:如果它导致关注微观优化,那就不会了。这是不健康的,完全是浪费时间。我真的不知道为什么人们喜欢这样的问题。@GabiPurcaru我喜欢这样的问题,因为他们奖励那些真正想了解他们的代码实际功能的人。研究计时差异和代码生成的人通常最终对该语言有着深刻的理解。@CatPlusPlus我真的不知道为什么人们会否决这样的问题。我在Python 2.7和3.2上做了同样的计时-结果与您在2.6中发现的结果几乎相同。这很奇怪,我在另一台机器上计时,根本没有什么不同。那为什么它会更快一点呢?我自己也很好奇。@卓尔:差别不大。如果我再测量一次,结果很可能是相反的。不仅如此,不是每种情况下论点都是1吗?所以它只是返回1?@pessimopotamus-是的,我为您发布的代码中有一个复制粘贴错误!:)如果能给我添加一条评论,我会很感激,这样我今天就不会问同样的问题了!:)
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
>>> [(k, hash(k) % 32) for k in globals().keys() ]
[('__builtins__', 8), ('__package__', 15), ('fact2', 25), ('__name__', 25), ('fact1', 26), ('__doc__', 29)]