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)]