2015-05-06 19 views
20

Montajdan, tamsayı bölme yönergelerinin hem bölümü hem de geri kalanı verdiğini hatırlıyorum. Yani, python'da yerleşik divmod() işlevi,% ve // ​​operatörlerini kullanmaktan daha iyi performansa sahip olacaktır (farz edelim ki, biri hem bölüm hem de geri kalanı gerektirir)? ölçmek içindivmod()% ve // ​​operatörlerini kullanmaktan daha mı hızlı?

q, r = divmod(n, d) 
q, r = (n // d, n % d) 
+5

Neden öğrenmek için 'timeit' kullanmıyoruz? –

+1

İşlev çağrısı ek yükü ('CALL_FUNCTION 'bytecode) muhtemelen ilk sürümde daha yavaş yürütmeye neden olur, ancak' timeit 'emin olmak için bir yoldur. –

+1

Hangi uygulama? –

cevap

30

(Macbook Pro 2.8GHz i7 tüm zamanlamaları) bilmektir: Eğer küresel her zaman yukarı bakmak gerekir çünkü

>>> import sys, timeit 
>>> sys.version_info 
sys.version_info(major=2, minor=7, micro=12, releaselevel='final', serial=0) 
>>> timeit.timeit('divmod(n, d)', 'n, d = 42, 7') 
0.1473848819732666 
>>> timeit.timeit('n // d, n % d', 'n, d = 42, 7') 
0.10324406623840332 

divmod() buradaki görevi bir dezavantaj olduğunu. (A timeit zaman deneme tüm değişkenler yereldir) yerel bir onu Bağlama biraz performansı artırır:

>>> timeit.timeit('dm(n, d)', 'n, d = 42, 7; dm = divmod') 
0.13460898399353027 

ama divmod() bir işlev çağrısı sırasında geçerli çerçeveyi korumak gerekmez çünkü operatörler hala kazanabilir yürütülür: // ve % varyantı daha opcodes kullanır, ancak CALL_FUNCTION baytkodu akıllıca bir ayı, performans

>>> import dis 
>>> dis.dis(compile('divmod(n, d)', '', 'exec')) 
    1   0 LOAD_NAME    0 (divmod) 
       3 LOAD_NAME    1 (n) 
       6 LOAD_NAME    2 (d) 
       9 CALL_FUNCTION   2 
      12 POP_TOP    
      13 LOAD_CONST    0 (None) 
      16 RETURN_VALUE   
>>> dis.dis(compile('(n // d, n % d)', '', 'exec')) 
    1   0 LOAD_NAME    0 (n) 
       3 LOAD_NAME    1 (d) 
       6 BINARY_FLOOR_DIVIDE 
       7 LOAD_NAME    0 (n) 
      10 LOAD_NAME    1 (d) 
      13 BINARY_MODULO  
      14 BUILD_TUPLE    2 
      17 POP_TOP    
      18 LOAD_CONST    0 (None) 
      21 RETURN_VALUE   

.


PyPy'de, küçük tamsayılar için çok fazla bir fark yoktur;

>>>> import platform, sys, timeit 
>>>> platform.python_implementation(), sys.version_info 
('PyPy', (major=2, minor=7, micro=10, releaselevel='final', serial=42)) 
>>>> timeit.timeit('divmod(n, d)', 'n, d = 42, 7', number=10**9) 
0.5659301280975342 
>>>> timeit.timeit('n // d, n % d', 'n, d = 42, 7', number=10**9) 
0.5471200942993164 

(ben fark gerçekten, PyPy blazingly ne kadar küçük göstermek için 1 milyar kadar tekrar sayısını krank zorunda: opcodes sahip küçük hız avantajı C tamsayı aritmetik hızlı bir şekilde yön altında eriyip gider Burada hızlı). sayılar olsun Ancak

, büyük, ülke mile tarafından divmod() galibiyet:

>>>> timeit.timeit('divmod(n, d)', 'n, d = 2**74207281 - 1, 26', number=100) 
17.620037078857422 
>>>> timeit.timeit('n // d, n % d', 'n, d = 2**74207281 - 1, 26', number=100) 
34.44323515892029 

(şimdi aşağı ayarlamak zorunda kıyasla 10 kat tekrarlama sayısı hobbs 'sayıları, sadece makul bir sürede sonuç elde etmek için).

Bunun nedeni, PyPy'nin artık tamsayıları C tamsayıları olarak kaldıramayacağıdır; Eğer sys.maxint ve sys.maxint + 1 kullanmak arasındaki zamanlamaları çarpıcı farkı görebilirsiniz: Eğer aritmetik işlemler çok hızlı işlev çağrılarına karşılaştırılır "küçük" yerli tamsayılar, kullanıyorsanız

>>>> timeit.timeit('divmod(n, d)', 'import sys; n, d = sys.maxint, 26', number=10**7) 
0.008622884750366211 
>>>> timeit.timeit('n // d, n % d', 'import sys; n, d = sys.maxint, 26', number=10**7) 
0.007693052291870117 
>>>> timeit.timeit('divmod(n, d)', 'import sys; n, d = sys.maxint + 1, 26', number=10**7) 
0.8396248817443848 
>>>> timeit.timeit('n // d, n % d', 'import sys; n, d = sys.maxint + 1, 26', number=10**7) 
1.0117690563201904 
+0

'' Divmod '' niçin büyük rakamlarla '// 've'% 'yi tamamen yok ettiğine dair bir fikrin var mı? –

+0

@ JimFasarakis-Hilliard: Çok sayıda destek. Artık bir C tamsayı değerinde temel bir C '&' maskeleme op kullanamazsınız, bu nedenle her zamanki optimizasyonlar pencereden dışarı çıkar. Int'nin büyüklüğü ile doğrudan orantılı olan bir algoritma kullanmanız ve divmodu yaparken bir keresinde bunu yapmanın tek tek operatörler ile kötü bir şekilde yaraladığı iki kez yapmak zorundasınız. –

12

Martijn cevabı doğrudur.

>>> import timeit 
>>> timeit.timeit('divmod(n, d)', 'n, d = 2**74207281 - 1, 26', number=1000) 
24.22666597366333 
>>> timeit.timeit('n // d, n % d', 'n, d = 2**74207281 - 1, 26', number=1000) 
49.517399072647095 

22 milyon basamaklı sayıyı bölerek, divmod iki kat daha hızlı Tahmin edebileceğiniz gibi, ayrı ayrı bölünme ve modül yapıyor neredeyse tam olarak: Ancak bigints ile bu tamamen farklı bir hikaye.

Makinemde, geçit 2^63 civarında bir yerde gerçekleşir, ama bunun için sözümü boşa harcamayın. Martijn'in dediği gibi, tedbir! Performans gerçekten önemli olduğunda, bir yerde doğru olanın başka bir yerde hala geçerli olacağını varsaymayın.

İlgili konular