Mengapa x**4.0
lebih cepat daripada x**4
di Python 3 * ?
int
Objek Python 3 adalah objek penuh yang dirancang untuk mendukung ukuran sewenang-wenang; karena fakta itu, mereka ditangani seperti itu pada level C (lihat bagaimana semua variabel dinyatakan sebagai PyLongObject *
tipe long_pow
). Ini juga membuat eksponensial mereka jauh lebih rumit dan melelahkan karena Anda perlu bermain-main dengan ob_digit
array yang digunakannya untuk mewakili nilainya untuk melakukannya. ( Sumber untuk pemberani. - Lihat: Memahami alokasi memori untuk bilangan bulat besar di Python untuk informasi lebih lanjut PyLongObject
.)
float
Objek Python , sebaliknya, dapat ditransformasikan ke double
tipe C (dengan menggunakan PyFloat_AsDouble
) dan operasi dapat dilakukan dengan menggunakan tipe-tipe asli tersebut . Ini bagus karena, setelah memeriksa tepi-kasus yang relevan, memungkinkan Python untuk menggunakan platformpow
( C pow
, yaitu ) untuk menangani eksponensial yang sebenarnya:
/* 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);
di mana iv
dan iw
asli PyFloatObject
s sebagai C double
s.
Untuk apa nilainya: Python 2.7.13
bagi saya adalah faktor yang 2~3
lebih cepat, dan menunjukkan perilaku terbalik.
Fakta sebelumnya juga menjelaskan perbedaan antara Python 2 dan 3 jadi, saya pikir saya akan membahas komentar ini juga karena itu menarik.
Di Python 2, Anda menggunakan int
objek lama yang berbeda dari int
objek di Python 3 (semua int
objek dalam 3.x PyLongObject
bertipe). Di Python 2, ada perbedaan yang tergantung pada nilai objek (atau, jika Anda menggunakan akhiran L/l
):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
The <type 'int'>
Anda lihat di sini melakukan hal yang sama float
s lakukan , itu akan aman diubah menjadi C long
ketika eksponensial dilakukan di atasnya (yang int_pow
juga mengisyaratkan compiler untuk menempatkan 'em di register jika dapat melakukannya, sehingga bisa membuat perbedaan) :
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
ini memungkinkan untuk mendapatkan kecepatan yang baik.
Untuk melihat bagaimana lambannya <type 'long'>
s dibandingkan dengan <type 'int'>
s, jika Anda membungkus x
nama dalam long
panggilan dengan Python 2 (pada dasarnya memaksanya untuk digunakan long_pow
seperti pada Python 3), kenaikan kecepatan menghilang:
# <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
Perhatikan bahwa, meskipun satu cuplikan mengubah int
ke long
sementara yang lain tidak (seperti yang ditunjukkan oleh @pydsinger), pemeran ini bukanlah kekuatan penyumbang di balik perlambatan. Implementasinya long_pow
adalah. (Hitung waktu pernyataan hanya dengan long(x)
melihat).
[...] itu tidak terjadi di luar loop. [...] Ada ide tentang itu?
Ini adalah pengoptimal lubang intip CPython melipat konstanta untuk Anda. Anda mendapatkan ketepatan waktu yang sama, karena tidak ada perhitungan yang sebenarnya untuk menemukan hasil eksponensial, hanya memuat nilai:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
Kode byte identik dihasilkan '4 ** 4.'
dengan satu-satunya perbedaan adalah bahwa LOAD_CONST
memuat float, 256.0
bukan int 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
Jadi waktunya identik.
* Semua hal di atas hanya berlaku untuk CPython, implementasi referensi dari Python. Implementasi lain mungkin berkinerja berbeda.