Dlaczego jest x**4.0
szybszy niż x**4
? Używam CPython 3.5.2.
$ python -m timeit "for x in range(100):" " x**4.0"
10000 loops, best of 3: 24.2 usec per loop
$ python -m timeit "for x in range(100):" " x**4"
10000 loops, best of 3: 30.6 usec per loop
Próbowałem zmienić siłę, o którą podniosłem, aby zobaczyć, jak to działa, i na przykład jeśli podbiję x do potęgi 10 lub 16, przeskakuje z 30 do 35, ale jeśli podbijam o 10,0 jako float, po prostu się porusza około 24,1 ~ 4.
Myślę, że ma to coś wspólnego z konwersją typu float i może z potęgami 2, ale tak naprawdę nie wiem.
Zauważyłem, że w obu przypadkach potęgi 2 są szybsze, myślę, że te obliczenia są bardziej natywne / łatwe dla interpretera / komputera. Ale mimo to, z pływakami, prawie się nie porusza. 2.0 => 24.1~4 & 128.0 => 24.1~4
ale 2 => 29 & 128 => 62
TigerhawkT3 wskazał, że nie dzieje się to poza pętlą. Sprawdziłem i sytuacja ma miejsce (z tego co widziałem) tylko wtedy, gdy podstawa się podnosi. Masz o tym jakiś pomysł?
python
performance
python-3.x
python-3.5
python-internals
arieljannai
źródło
źródło
x**4.0
i 3,9 dlax**4
.Odpowiedzi:
int
Obiekty Pythona 3 są pełnoprawnymi obiektami zaprojektowanymi do obsługi dowolnego rozmiaru; w związku z tym są traktowane jako takie na poziomie C (zobacz, jak wszystkie zmienne są deklarowane jakoPyLongObject *
typ inlong_pow
). To również sprawia, że ich potęgowanie jest znacznie trudniejsze i bardziej żmudne, ponieważ musisz bawić sięob_digit
tablicą, której używa do reprezentowania jej wartości, aby ją wykonać. ( Źródło dla odważnych. - Zobacz: Zrozumienie alokacji pamięci dla dużych liczb całkowitych w Pythonie, aby uzyskać więcej informacji na tematPyLongObject
s.)float
Wręcz przeciwnie, obiekty Pythona można przekształcić dodouble
typu C (przy użyciuPyFloat_AsDouble
), a operacje można wykonywać przy użyciu tych typów natywnych . To jest wielki , ponieważ po sprawdzeniu odpowiednich krawędziowych przypadkach pozwala Pythona do korzystania z platformypow
( C użytkownikapow
, że jest ), aby obsłużyć rzeczywisty potęgowanie:gdzie
iv
iiw
są naszymi oryginałamiPyFloatObject
jako Cdouble
s.Poprzedni fakt wyjaśnia również rozbieżność między Pythonem 2 i 3, więc pomyślałem, że odniosę się również do tego komentarza, ponieważ jest interesujący.
W Pythonie 2 używasz starego
int
obiektu, który różni się odint
obiektu w Pythonie 3 (wszystkieint
obiekty w 3.x sąPyLongObject
typu). W Pythonie 2 istnieje różnica, która zależy od wartości obiektu (lub, jeśli używasz przyrostkaL/l
):To,
<type 'int'>
co widzisz tutaj, robi to samo, cofloat
robi , jest bezpiecznie konwertowane na C,long
gdy wykonywane jest na nim potęgowanie (int_pow
podpowiada również kompilatorowi, aby umieścić je w rejestrze, jeśli może to zrobić, więc może to zrobić różnicę) :pozwala to na dobry przyrost prędkości.
Aby zobaczyć, jak powolne
<type 'long'>
są s w porównaniu do<type 'int'>
s, jeśli umieściszx
nazwę wlong
wywołaniu w Pythonie 2 (zasadniczo zmuszając ją do użycialong_pow
jak w Pythonie 3), przyrost prędkości znika:Należy wziąć pod uwagę, że choć jeden snippet przekształca się
int
dolong
podczas gdy inne nie (jak podkreślił @pydsinger), to obsada nie jest przyczynianie się siłą spowolnienia. Wdrożenielong_pow
is. (Zmierz czas tylko z wyrażeniami,long(x)
aby zobaczyć).To jest optymalizator wizualizacji CPythona składający stałe za Ciebie. W obu przypadkach otrzymujesz te same dokładne czasy, ponieważ nie ma rzeczywistych obliczeń, które pozwolą znaleźć wynik potęgowania, tylko ładowanie wartości:
Generowany jest identyczny kod bajtowy,
'4 ** 4.'
z tą różnicą, żeLOAD_CONST
ładuje zmiennoprzecinkowy256.0
zamiast int256
:Więc czasy są identyczne.
* Wszystkie powyższe dotyczą wyłącznie CPythona, referencyjnej implementacji Pythona. Inne implementacje mogą działać inaczej.
źródło
range
, ponieważ tylko**
sama operacja nie daje różnicy między liczbami całkowitymi i zmiennoprzecinkowymi.4**4
jest tak samo szybka jak4**4.0
), a ta odpowiedź w ogóle tego nie dotyka.dis(compile('4 ** 4', '', 'exec'))
), więc czas powinien być dokładnie taki sam.long(x)**2.
jest nadal szybsza niżlong(x)**2
4-5 razy. (Jednak nie jeden z przeciwników)<type 'long'>
typu w Pythonie 3 jest prawdopodobnie wyjaśniona przez wysiłki podjęte w celu uproszczenia języka. Jeśli możesz mieć jeden typ do reprezentowania liczb całkowitych, jest to łatwiejsze do zarządzania niż dwa (i martwienie się o konwersję z jednego na drugi, gdy to konieczne, zdezorientowanie użytkowników itp.) Przyrost prędkości ma drugorzędne znaczenie. Część dotycząca uzasadnienia w PEP 237 również oferuje więcej informacji.Jeśli spojrzymy na kod bajtowy, zobaczymy, że wyrażenia są całkowicie identyczne. Jedyną różnicą jest rodzaj stałej, która będzie argumentem
BINARY_POWER
. Więc z całą pewnością jest toint
spowodowane konwersją na liczbę zmiennoprzecinkową w dół wiersza.Aktualizacja: spójrzmy na Objects / abstract.c w kodzie źródłowym CPython:
PyNumber_Power
połączeńternary_op
, które są zbyt długie, aby je wkleić, więc oto link .Wywołuje
nb_power
przedziałx
, przekazujący
jako argument.Wreszcie w
float_pow()
wierszu 686 Objects / floatobject.c widzimy, że argumenty są konwertowane na Cdouble
tuż przed samą operacją:źródło
float_pow
wtedy, gdy to nawet nie działa w powolnym przypadku?4**4
i spasuj na4**4.0
stałe. To zupełnie odrębny efekt.Ponieważ jeden jest poprawny, drugi jest przybliżeniem.
źródło