考虑以下 Python 2 代码

from timeit import default_timer 
 
def floor(): 
    for _ in xrange(10**7): 
        1 * 12 // 39 * 2 // 39 * 23 - 234 
 
def normal(): 
    for _ in xrange(10**7): 
        1 * 12 / 39 * 2 / 39 * 23 - 234 
 
t1 = default_timer() 
floor() 
t2 = default_timer() 
normal() 
t3 = default_timer() 
 
print 'Floor  %.3f' % (t2 - t1) 
print 'Normal %.3f' % (t3 - t2) 

在我的电脑上的输出是

Floor  0.254 
Normal 1.766 

那么,为什么下限除法运算符 // 比普通除法运算符 / 更快,而两者都在做同样的事情?

请您参考如下方法:

Python 解释器在 floor 中预先计算循环内的表达式,但在 normal 中没有。

这是地板的代码:

>>> dis.dis(floor) 
 
  5           0 SETUP_LOOP              24 (to 27) 
              3 LOAD_GLOBAL              0 (xrange) 
              6 LOAD_CONST               9 (10000000) 
              9 CALL_FUNCTION            1 
             12 GET_ITER             
        >>   13 FOR_ITER                10 (to 26) 
             16 STORE_FAST               0 (_) 
 
  6          19 LOAD_CONST              15 (-234) 
             22 POP_TOP              
             23 JUMP_ABSOLUTE           13 
        >>   26 POP_BLOCK            
        >>   27 LOAD_CONST               0 (None) 
             30 RETURN_VALUE         

可以看到表达式已经计算好了 LOAD_CONST 15 (-234)

对于 normal 也是一样的:

>>> dis.dis(normal) 
 
  9           0 SETUP_LOOP              44 (to 47) 
              3 LOAD_GLOBAL              0 (xrange) 
              6 LOAD_CONST               9 (10000000) 
              9 CALL_FUNCTION            1 
             12 GET_ITER             
        >>   13 FOR_ITER                30 (to 46) 
             16 STORE_FAST               0 (_) 
 
 10          19 LOAD_CONST              10 (12) 
             22 LOAD_CONST               5 (39) 
             25 BINARY_DIVIDE        
             26 LOAD_CONST               6 (2) 
             29 BINARY_MULTIPLY      
             30 LOAD_CONST               5 (39) 
             33 BINARY_DIVIDE        
             34 LOAD_CONST               7 (23) 
             37 BINARY_MULTIPLY      
             38 LOAD_CONST               8 (234) 
             41 BINARY_SUBTRACT      
             42 POP_TOP              
             43 JUMP_ABSOLUTE           13 
        >>   46 POP_BLOCK            
        >>   47 LOAD_CONST               0 (None) 
             50 RETURN_VALUE         

这次只简化了部分计算(例如:省略了初始的1 *),大部分操作都在运行时进行。

看起来 Python 2.7 不会执行包含不明确的 / 运算符的常量折叠(根据其操作数,它可能是整数或浮点除法)。在程序顶部添加 from __future__ import division 会导致常量在 normal 中折叠,就像在 floor 中一样(尽管结果当然是不同的,因为现在 / 是浮点除法)。

normal 
 10           0 SETUP_LOOP              24 (to 27) 
              3 LOAD_GLOBAL              0 (xrange) 
              6 LOAD_CONST               9 (10000000) 
              9 CALL_FUNCTION            1 
             12 GET_ITER             
        >>   13 FOR_ITER                10 (to 26) 
             16 STORE_FAST               0 (_) 
 
 11          19 LOAD_CONST              15 (-233.6370808678501) 
             22 POP_TOP              
             23 JUMP_ABSOLUTE           13 
        >>   26 POP_BLOCK            
        >>   27 LOAD_CONST               0 (None) 
             30 RETURN_VALUE         

解释器不能使用默认的/ 运算符进行常量折叠,但事实并非如此。也许该代码是从 Python 3 向后移植的,并且认为让它与模棱两可的除法运算符一起工作并不重要。


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!