Lý do (hơi bất ngờ) cho kết quả của bạn là Python dường như gấp các biểu thức hằng số liên quan đến phép nhân và lũy thừa dấu phẩy động, nhưng không phải phép chia. math.sqrt()
hoàn toàn là một con thú khác vì không có mã bytecode nào cho nó và nó liên quan đến một lệnh gọi hàm.
Trên Python 2.6.5, đoạn mã sau:
x1 = 1234567890.0 / 4.0
x2 = 1234567890.0 * 0.25
x3 = 1234567890.0 ** 0.5
x4 = math.sqrt(1234567890.0)
biên dịch thành các mã byte sau:
4 0 LOAD_CONST 1 (1234567890.0)
3 LOAD_CONST 2 (4.0)
6 BINARY_DIVIDE
7 STORE_FAST 0 (x1)
5 10 LOAD_CONST 5 (308641972.5)
13 STORE_FAST 1 (x2)
6 16 LOAD_CONST 6 (35136.418286444619)
19 STORE_FAST 2 (x3)
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)
Như bạn có thể thấy, phép nhân và phép lũy thừa hoàn toàn không mất thời gian vì chúng được thực hiện khi mã được biên dịch. Việc phân chia mất nhiều thời gian hơn vì nó xảy ra trong thời gian chạy. Căn bậc hai không chỉ là phép tính tốn kém nhất về mặt tính toán trong bốn phép toán, nó còn chịu nhiều chi phí khác nhau mà các phép khác không (tra cứu thuộc tính, gọi hàm, v.v.).
Nếu bạn loại bỏ ảnh hưởng của việc gấp liên tục, sẽ có rất ít thứ để tách phép nhân và chia:
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)
thực sự nhanh hơn một chút x ** 0.5
, có lẽ vì đây là trường hợp đặc biệt của trường hợp thứ hai và do đó có thể được thực hiện hiệu quả hơn, bất chấp các chi phí chung:
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
chỉnh sửa 2011-11-16: Việc gấp biểu thức liên tục được thực hiện bởi trình tối ưu hóa lỗ nhìn trộm của Python. Mã nguồn ( peephole.c
) chứa nhận xét sau giải thích tại sao phép chia hằng không được gấp lại:
case BINARY_DIVIDE:
return 0;
Các -Qnew
lá cờ cho phép "phân chia đúng" quy định tại PEP 238 .