(Nieco nieoczekiwanym) powodem twoich wyników jest to, że Python wydaje się składać wyrażenia stałe, w tym mnożenie zmiennoprzecinkowe i potęgowanie, ale nie dzielenie. math.sqrt()
to zupełnie inna bestia, ponieważ nie ma dla niej kodu bajtowego i wymaga wywołania funkcji.
na Python 2.6.5, następujący kod:
x1 = 1234567890.0/4.0
x2 = 1234567890.0 * 0.25
x3 = 1234567890.0 ** 0.5
x4 = math.sqrt(1234567890.0)
kompiluje się do następujących bytecodes:
# x1 = 1234567890.0/4.0
4 0 LOAD_CONST 1 (1234567890.0)
3 LOAD_CONST 2 (4.0)
6 BINARY_DIVIDE
7 STORE_FAST 0 (x1)
# x2 = 1234567890.0 * 0.25
5 10 LOAD_CONST 5 (308641972.5)
13 STORE_FAST 1 (x2)
# x3 = 1234567890.0 ** 0.5
6 16 LOAD_CONST 6 (35136.418286444619)
19 STORE_FAST 2 (x3)
# x4 = math.sqrt(1234567890.0)
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)
Jak widać, mnożenie i potęgowanie brać w ogóle czasu, ponieważ” zrobić ponownie, gdy kod zostanie skompilowany. Podział trwa dłużej, ponieważ dzieje się to w czasie wykonywania. Pierwiastek kwadratowy jest nie tylko najbardziej kosztowną obliczeniowo operacją czwórki, ale także powoduje różne koszty ogólne, których inni nie robią (wyszukiwanie atrybutów, wywoływanie funkcji itp.).
Jeśli wyeliminować efekt ciągłego zwijania, tam trochę oddzielić mnożenie i dzielenie:
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)
jest faktycznie nieco szybciej niż x ** 0.5
, przypuszczalnie dlatego, że jest szczególnym przypadkiem tego ostatniego, a zatem być bardziej efektywnie zrobić, pomimo ogólnych:
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
edycji 2011-11-16: Stała składany ekspresja jest wykonywana przez PE Pythona Optymalizator ephole. Kod źródłowy (peephole.c
) zawiera następujące komentarz, który wyjaśnia dlaczego stały podział nie jest złożona:
case BINARY_DIVIDE:
/* Cannot fold this operation statically since
the result can depend on the run-time presence
of the -Qnew flag */
return 0;
-Qnew
flaga umożliwia podział „prawdziwą” zdefiniowanego w PEP 238.
Odpowiedź, którą zaakceptowałeś na swoje pytanie (które, jak zakładam, odpowiada na twoje prawdziwe pytanie), nie ma wiele wspólnego z tytułem pytania. Czy możesz go edytować, aby mieć coś wspólnego ze stałym składaniem? –
@ZanLynx - Hi. Czy mógłbyś wyjaśnić? Uważam, że tytuł pytania dokładnie wyraża to, co chciałem wiedzieć (dlaczego X jest szybszy od Y), a odpowiedź, którą wybrałem, dokładnie to brzmi ... Wydaje mi się idealnie pasować do mnie ... ale może coś przeoczyłem? – mac
Funkcje mnożenia i zasilania są zawsze szybsze niż funkcje dzielenia i sqrt() ze względu na ich charakter. Podział i operacje root'a zazwyczaj muszą korzystać z szeregu dokładniejszych i dokładniejszych przybliżeń i nie mogą przejść bezpośrednio do właściwej odpowiedzi, jak mnożenie. –