Tại sao x**4.0
nhanh hơn x**4
trong Python 3 * ?
Các int
đối tượng Python 3 là một đối tượng chính thức được thiết kế để hỗ trợ kích thước tùy ý; do thực tế đó, chúng được xử lý như vậy ở cấp độ C (xem cách tất cả các biến được khai báo là PyLongObject *
kiểu trong long_pow
). Điều này cũng làm cho phép lũy thừa của chúng trở nên phức tạp và tẻ nhạt hơn rất nhiều vì bạn cần phải chơi xung quanh với ob_digit
mảng mà nó sử dụng để biểu thị giá trị của nó để thực hiện nó. ( Nguồn cho người dũng cảm. - Xem: Tìm hiểu phân bổ bộ nhớ cho số nguyên lớn trong Python để biết thêm về PyLongObject
s.)
float
Ngược lại, các đối tượng Python có thể được chuyển đổi thành double
loại C (bằng cách sử dụng PyFloat_AsDouble
) và các hoạt động có thể được thực hiện bằng các loại gốc đó . Này là rất tốt bởi vì, sau khi kiểm tra cho cạnh các trường hợp có liên quan, nó cho phép Python để sử dụng các nền tảngpow
( C pow
, có nghĩa là ) để xử lý các lũy thừa thực tế:
/* 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);
ở đâu iv
và iw
là bản gốc của chúng tôi PyFloatObject
là C double
s.
Đối với những gì nó có giá trị: Python 2.7.13
đối với tôi là một yếu tố 2~3
nhanh hơn và cho thấy hành vi nghịch đảo.
Thực tế trước đây cũng giải thích sự khác biệt giữa Python 2 và 3 vì vậy, tôi nghĩ tôi cũng sẽ giải quyết nhận xét này vì nó rất thú vị.
Trong Python 2, bạn đang sử dụng int
đối tượng cũ khác với int
đối tượng trong Python 3 (tất cả int
các đối tượng trong 3.x là PyLongObject
loại). Trong Python 2, có một sự phân biệt phụ thuộc vào giá trị của đối tượng (hoặc, nếu bạn sử dụng hậu tố L/l
):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
Các <type 'int'>
bạn thấy ở đây làm điều tương tự float
s làm , nó được một cách an toàn chuyển đổi thành một C long
khi lũy thừa được thực hiện trên nó (The int_pow
cũng gợi ý trình biên dịch để đưa 'em trong một thanh ghi nếu nó có thể làm như vậy, để có thể tạo sự khác biệt) :
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
điều này cho phép đạt được tốc độ tốt.
Để xem mức độ chậm chạp <type 'long'>
so với <type 'int'>
s, nếu bạn gói x
tên trong một long
cuộc gọi trong Python 2 (về cơ bản buộc nó phải sử dụng long_pow
như trong Python 3), tốc độ tăng sẽ biến mất:
# <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
Xin lưu ý rằng, mặc dù một đoạn biến đổi int
thành long
trong khi đoạn kia không (như được chỉ ra bởi @pydsinger), dàn diễn viên này không phải là lực lượng đóng góp đằng sau sự chậm lại. Việc thực hiện long_pow
là. (Thời gian báo cáo chỉ long(x)
để xem).
[...] Nó không xảy ra bên ngoài vòng lặp. [...] Có ý kiến gì về điều đó không?
Đây là trình tối ưu hóa lổ nhìn trộm của CPython gấp các hằng số cho bạn. Bạn cũng có được thời gian chính xác như nhau vì không có tính toán thực tế để tìm kết quả của lũy thừa, chỉ tải các giá trị:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
Mã byte giống hệt nhau được tạo ra '4 ** 4.'
với sự khác biệt duy nhất là LOAD_CONST
tải float 256.0
thay vì 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
Vì vậy, thời gian là giống hệt nhau.
* Tất cả những điều trên chỉ áp dụng cho CPython, triển khai tham chiếu của Python. Các triển khai khác có thể thực hiện khác nhau.