Der (etwas unerwartete) Grund für Ihre Ergebnisse ist, dass Python konstante Ausdrücke zu falten scheint, die Gleitkomma-Multiplikation und Exponentiation beinhalten, aber keine Division. math.sqrt()
ist ein ganz anderes Tier, da es keinen Bytecode dafür gibt und es einen Funktionsaufruf beinhaltet.
In Python 2.6.5 den folgenden Code:
x1 = 1234567890.0 / 4.0
x2 = 1234567890.0 * 0.25
x3 = 1234567890.0 ** 0.5
x4 = math.sqrt(1234567890.0)
Kompiliert zu folgenden Bytecodes:
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)
Wie Sie sehen können, nehmen Multiplikation und Exponentiation überhaupt keine Zeit in Anspruch, da sie beim Kompilieren des Codes abgeschlossen sind. Die Teilung dauert länger, da sie zur Laufzeit erfolgt. Die Quadratwurzel ist nicht nur die rechenintensivste der vier Operationen, sondern verursacht auch verschiedene Gemeinkosten, die die anderen nicht verursachen (Attributsuche, Funktionsaufruf usw.).
Wenn Sie den Effekt der konstanten Faltung eliminieren, gibt es wenig, um Multiplikation und Division zu trennen:
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)
ist tatsächlich ein bisschen schneller als x ** 0.5
, vermutlich weil es sich um einen Sonderfall handelt und daher trotz des Overheads effizienter durchgeführt werden kann:
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
edit 2011-11-16: Das Falten konstanter Ausdrücke wird vom Python-Gucklochoptimierer durchgeführt. Der Quellcode ( peephole.c
) enthält den folgenden Kommentar, der erklärt, warum die konstante Division nicht gefaltet ist:
case BINARY_DIVIDE:
return 0;
Das -Qnew
Flag aktiviert die in PEP 238 definierte "wahre Teilung" .