2016-04-24 53 views
1

考慮下面的Python 2代碼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 

那麼,爲什麼地板除法運算//比正常的除法運算/更快當兩個他們正在做同樣的事情?

回答

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中一樣(雖然結果當然不同,因爲/現在是float division)。

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移植過來的,並且使它與模糊分割運算符一起工作並不重要。

+0

是的,但爲什麼會發生? – avamsi

+0

@avamsi我調查了一點,並添加了一些更多的細節。 –

1

可以使用dis module檢查的特定蟒函數的編譯的字節代碼:

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  
0

「產生相同的結果」並不意味着「以相同的方式實施」。 還要注意的是,這些操作並不總是產生相同的結果,如下解釋:

Why Python's Integer Division Floors

所以性能測量是非常依賴於實現的。 通常硬件浮點除法需要比整數除法更長的時間。 這可能是蟒蛇經典部門(你稱爲正常的)由硬件浮點除法實現,只在最後階段被截斷回整數,而真正的除法(被你稱爲floored)是使用硬件int分割實現的速度要快很多。