Dlaczego jest x**4.0 szybszy niż x**4w Pythonie 3 * ?
intObiekty 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 jako PyLongObject *typ in long_pow). To również sprawia, że ich potęgowanie jest znacznie trudniejsze i bardziej żmudne, ponieważ musisz bawić się ob_digittablicą, 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 temat PyLongObjects.)
floatWręcz przeciwnie, obiekty Pythona można przekształcić do doubletypu C (przy użyciu PyFloat_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żytkownika pow, że jest ), aby obsłużyć rzeczywisty potęgowanie:
/* 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);
gdzie ivi iwsą naszymi oryginałami PyFloatObjectjako C doubles.
Bo to jest warte: Python jest 2.7.13dla mnie czynnikiem 2~3szybszym i wykazuje odwrotne zachowanie.
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 intobiektu, który różni się od intobiektu w Pythonie 3 (wszystkie intobiekty w 3.x są PyLongObjecttypu). W Pythonie 2 istnieje różnica, która zależy od wartości obiektu (lub, jeśli używasz przyrostka L/l):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
To, <type 'int'>co widzisz tutaj, robi to samo, co floatrobi , jest bezpiecznie konwertowane na C, long gdy wykonywane jest na nim potęgowanie ( int_powpodpowiada również kompilatorowi, aby umieścić je w rejestrze, jeśli może to zrobić, więc może to zrobić różnicę) :
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
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ścisz xnazwę w longwywołaniu w Pythonie 2 (zasadniczo zmuszając ją do użycia long_powjak w Pythonie 3), przyrost prędkości znika:
# <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
Należy wziąć pod uwagę, że choć jeden snippet przekształca się intdo longpodczas gdy inne nie (jak podkreślił @pydsinger), to obsada nie jest przyczynianie się siłą spowolnienia. Wdrożenie long_powis. (Zmierz czas tylko z wyrażeniami, long(x)aby zobaczyć).
[…] nie dzieje się to poza pętlą. […] Masz o tym jakiś pomysł?
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:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
Generowany jest identyczny kod bajtowy, '4 ** 4.'z tą różnicą, że LOAD_CONSTładuje zmiennoprzecinkowy 256.0zamiast 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
Więc czasy są identyczne.
* Wszystkie powyższe dotyczą wyłącznie CPythona, referencyjnej implementacji Pythona. Inne implementacje mogą działać inaczej.