2011-11-09 8 views
79

anladım şu: AyrıcaPython: neden * ve ** daha hızlı ve/veya sqrt()? kodumu optimize ederken

>>> from timeit import Timer as T 
>>> T(lambda : 1234567890/4.0).repeat() 
[0.22256922721862793, 0.20560789108276367, 0.20530295372009277] 
>>> from __future__ import division 
>>> T(lambda : 1234567890/4).repeat() 
[0.14969301223754883, 0.14155197143554688, 0.14141488075256348] 
>>> T(lambda : 1234567890 * 0.25).repeat() 
[0.13619112968444824, 0.1281130313873291, 0.12830305099487305] 

ve:

>>> from math import sqrt 
>>> T(lambda : sqrt(1234567890)).repeat() 
[0.2597470283508301, 0.2498021125793457, 0.24994492530822754] 
>>> T(lambda : 1234567890 ** 0.5).repeat() 
[0.15409398078918457, 0.14059877395629883, 0.14049601554870605] 

Ben piton C uygulandığı yol ile ilgisi var varsayalım, ama herkes ister misiniz acaba neden böyle olduğunu açıklamak için? Sonuçlarınız için

+0

Eğer (keşke gerçek soru cevaplar varsayıyorum) Sorunuza kabul cevap soru başlığıyla yapmak çok yok. Sürekli katlama ile ilgili bir şey yapmak için düzenleyebilir misiniz? –

+1

@ZanLynx - Merhaba. Açıklığa kavuşturmak ister misiniz? Ben soru başlığı ben (X, Y daha hızlıdır neden) bilmek istedim tam olarak ne ifade ettiğini bulmak ve seçilen cevabı tam olarak yapar o ... mükemmel bir maç gibi geliyor bana ... ama belki bir şey bakan ben? – mac

+8

Çarpma ve güç fonksiyonları her zaman çünkü doğaları bölünmesi ve sqrt() fonksiyonu daha hızlıdır. Bölüm ve kök operasyonları genellikle bir dizi daha ince ve daha ince yaklaşımlar kullanmak zorundadır ve doğrudan doğruya çarpma tenekesi gibi doğru cevaplara gidemez. –

cevap

112

(biraz beklenmedik) nedeni Python kayan nokta çarpma ve üs değil, bölme içeren sabit ifadeleri katlamak gibi görünüyor olmasıdır. Orada onun için hiçbir baytkodu ve o bir işlev çağrısını içerir beri math.sqrt() tamamen farklı bir canavar.

# x1 = 1234567890.0/4.0 
    4   0 LOAD_CONST    1 (1234567890.0) 
       3 LOAD_CONST    2 (4.0) 
       6 BINARY_DIVIDE  
       7 STORE_FAST    0 (x1) 

    # x2 = 1234567890.0 * 0.25 
    5   10 LOAD_CONST    5 (308641972.5) 
      13 STORE_FAST    1 (x2) 

    # x3 = 1234567890.0 ** 0.5 
    6   16 LOAD_CONST    6 (35136.418286444619) 
      19 STORE_FAST    2 (x3) 

    # x4 = math.sqrt(1234567890.0) 
    7   22 LOAD_GLOBAL    0 (math) 
      25 LOAD_ATTR    1 (sqrt) 
      28 LOAD_CONST    1 (1234567890.0) 
      31 CALL_FUNCTION   1 
      34 STORE_FAST    3 (x4) 

Gördüğünüz gibi, çarpma ve üs hiç onlar yapmaya başladığımdan beri hiçbir zaman alır:

x1 = 1234567890.0/4.0 
x2 = 1234567890.0 * 0.25 
x3 = 1234567890.0 ** 0.5 
x4 = math.sqrt(1234567890.0) 

aşağıdaki baytkodlarýna derler: Python 2.6.5, aşağıdaki kodu Açık

kod derlendiğinde yeniden yapılır. Bölüm, çalışma zamanında gerçekleştiği için daha uzun sürer. Karekök sadece dört en hesaplama pahalı bir işlemdir, aynı zamanda diğerleri (öznitelik araması, vb fonksiyon çağrısı) do çeşitli giderlerini doğurur. Eğer sürekli katlanması etkisini ortadan kaldırmak ise

, çarpma ve bölme ayırmak için küçük nokta vardır: bu ikincisi özel bir hali olduğu için muhtemelen, x ** 0.5 göre biraz daha hızlı

In [16]: x = 1234567890.0 

In [17]: %timeit x/4.0 
10000000 loops, best of 3: 87.8 ns per loop 

In [18]: %timeit x * 0.25 
10000000 loops, best of 3: 91.6 ns per loop 

math.sqrt(x) aslında bulunur ve dolayısıyla

In [19]: %timeit x ** 0.5 
1000000 loops, best of 3: 211 ns per loop 

In [20]: %timeit math.sqrt(x) 
10000000 loops, best of 3: 181 ns per loop 

düzenlemek 2011-11-16: Sabit ifade katlama Python'un pe tarafından yapılır giderler rağmen, daha etkin yapılması epim optimize edici.

case BINARY_DIVIDE: 
     /* Cannot fold this operation statically since 
      the result can depend on the run-time presence 
      of the -Qnew flag */ 
     return 0; 

-Qnew bayrağı PEP 238 tanımlanan "gerçek bölünme" sağlar: kaynak kodu (peephole.c) sabit bölünme katlanmış değil neden anlatıldığı şu yorumunu içermektedir.

+2

Belki de "sıfıra bölme" sıfıra karşı "koruyor"? – hugomg

+2

@missingno: Her iki argümanın derlenme zamanında bilindiği ve sonuç olarak (yani: + inf, -inf, NaN) olduğu için böyle bir "korumanın" neden gerekli olduğu açık değildir. – NPE

+1

belki de "__future__ import division" testinden benzer bir yöntem kullanır. – Simon

İlgili konular