Python 2:为什么分区运算符比普通分区运算符更快?

时间:2016-04-24 06:44:35

标签: python python-2.7

考虑以下Python 2代码

numbers = []
def excludeMax():
   while True:
       result = input('Enter next number or end:')
       if (result == 'end'):
           break
       else:
           numbers.append(result)
   numbers.sort()
   sum_numbers = sum(numbers[:-1])

我的电脑上的输出是

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 比普通除法运算符//更快?

3 个答案:

答案 0 :(得分:4)

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反向移植的,并且使它与模糊的除法运算符一起工作并不重要。

答案 1 :(得分:2)

您可以使用dis module

检查特定python函数的已编译字节码
def floor(): 
  12 // 39

def normal(): 
  12 / 39

>>> dis.dis(floor)
  2           0 LOAD_CONST               3 (0)
              3 POP_TOP             
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE        

>>> dis.dis(normal)
  2           0 LOAD_CONST               1 (12)
              3 LOAD_CONST               2 (39)
              6 BINARY_DIVIDE       
              7 POP_TOP             
              8 LOAD_CONST               0 (None)
             11 RETURN_VALUE     

答案 2 :(得分:0)

"产生相同的结果"并不暗示"以同样的方式实施"。 另请注意,这些运营商并不总是产生与此处所述相同的结果:

Why Python's Integer Division Floors

因此,性能测量几乎取决于实现。 通常硬件浮点除法比整数除法要长。 可能是python经典除法(由你正常引用)是由硬件浮点除法实现的,并且只在最后阶段被截断为整数,而真正的除法(由你称为floored)是使用硬件int division实现的。快得多。