Neden Python 3 * ' den x**4.0
daha hızlı ?x**4
Python 3 int
nesneleri, keyfi bir boyutu desteklemek için tasarlanmış tam teşekküllü bir nesnedir; Bu nedenle, C seviyesinde bu şekilde ele alınırlar (tüm değişkenlerin nasıl PyLongObject *
tür olarak bildirildiğine bakın long_pow
). Bu, üslerini çok daha zor ve sıkıcı hale getirir, çünkü ob_digit
değerini gerçekleştirmek için kullandığı dizi ile oynamanız gerekir. ( Cesurlar için kaynak. - Bkz . Daha fazla bilgi için Python'daki büyük tamsayılar için bellek ayırmayı anlamaPyLongObject
.)
Piton float
amaçları, tam tersine, transforme edilebilir bir C double
(kullanarak türü PyFloat_AsDouble
) ve işlemler yapılabilir , bu doğal türleri kullanarak . Bu harika alakalı kenar-durumlar için kontrol ettikten sonra, bu için Python verir, çünkü platformları kullanabilirsinizpow
( C'ler pow
olduğunu ) fiili üs ele:
/* Now iv and iw are finite, iw is nonzero, and iv is
* positive and not equal to 1.0. We finally allow
* the platform pow to step in and do the rest.
*/
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw);
nerede iv
ve iw
bizim orijinal PyFloatObject
C olarak double
s.
Değeri 2.7.13
için: Benim için Python 2~3
daha hızlı bir faktördür ve ters davranışı gösterir.
Önceki gerçek de Python 2 ve 3 arasındaki tutarsızlığı açıklıyor , bu yüzden ilginç olduğu için bu yorumu da ele alacağımı düşündüm.
Python 2'de, Python 3'teki nesneden int
farklı olan eski nesneyi kullanıyorsunuz int
( int
3.x'teki tüm nesneler türdedir PyLongObject
). Python 2'de, nesnenin değerine (veya sonek kullanırsanız L/l
) bağlı bir ayrım vardır :
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
<type 'int'>
Burada gördüğünüz aynı şeyi yapar float
ler yapmak güvenle bir C dönüştürülmüş olur, long
üs alma üzerine yapılır ( int_pow
bunu yapabiliyorsa, böylece aynı zamanda bir kayıt onları koymak için derleyici ipuçları verebilir bir fark yaratmak) :
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
bu iyi bir hız kazancı sağlar.
Durgun s'ların <type 'long'>
s ile karşılaştırıldığında ne kadar olduğunu görmek için <type 'int'>
, x
adı long
Python 2'deki bir çağrıda tamamladıysanız (esas long_pow
olarak Python 3'teki gibi kullanmaya zorlarsanız), hız kazancı kaybolur:
# <type 'int'>
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"
10000 loops, best of 3: 116 usec per loop
# <type 'long'>
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop
Not alın, bu bir pasajı dönüşümleri olsa int
için long
(@pydsinger tarafından sivri out gibi) diğer yaptığı, bu döküm yavaşlamanın arkasında katkıda güç değildir. Uygulamasıdır long_pow
. (İfadeleri yalnızca long(x)
görmek için zamanlayın).
[...] döngü dışında olmaz. [...] Bunun hakkında bir fikrin var mı?
Bu, sabitleri sizin için katlayan CPython'un gözetleme deliği optimize edicisidir. Her iki durumda da aynı kesin zamanlamaları elde edersiniz, çünkü üstelimin sonucunu bulmak için gerçek bir hesaplama yoktur, sadece değerlerin yüklenmesi:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
Özdeş bayt kodu, '4 ** 4.'
tek fark int yerine LOAD_CONST
float yükler olmasıdır :256.0
256
dis.dis(compile('4 ** 4.', '', 'exec'))
1 0 LOAD_CONST 3 (256.0)
2 POP_TOP
4 LOAD_CONST 2 (None)
6 RETURN_VALUE
Yani zamanlar aynı.
* Yukarıdakilerin tümü sadece Python'un referans uygulaması olan CPython için geçerlidir. Diğer uygulamalar farklı performans gösterebilir.