Warum ist x**4.0 schneller als x**4in Python 3 * ?
Python 3- intObjekte sind vollwertige Objekte, die eine beliebige Größe unterstützen. Aus diesem Grund werden sie auf C-Ebene als solche behandelt (siehe, wie alle Variablen als PyLongObject *Typ-In deklariert werden long_pow). Dies macht ihre Potenzierung auch viel schwieriger und langwieriger, da Sie mit dem ob_digitArray herumspielen müssen, das es verwendet, um seinen Wert darzustellen, um es auszuführen. ( Quelle für die Mutigen. - Siehe auch : Grundlegendes zu Speicherzuweisung für große Zahlen in Python für mehr auf PyLongObject. S)
floatIm Gegensatz dazu können Python- Objekte double(mithilfe von PyFloat_AsDouble) in einen C- Typ umgewandelt werden, und Operationen können mit diesen nativen Typen ausgeführt werden . Das ist großartig , weil nach dem für relevante Ränder Fälle überprüft, es Python ermöglicht die Plattformenpow ( C ist pow, das ist ) , um die tatsächliche Potenzierung zu handhaben :
/* 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);
wo ivund iwsind unsere originalen PyFloatObjects als C doubles.
Für das, was es wert ist: Python ist 2.7.13für mich ein Faktor 2~3schneller und zeigt das umgekehrte Verhalten.
Die vorherige Tatsache erklärt auch die Diskrepanz zwischen Python 2 und 3, daher dachte ich, ich würde diesen Kommentar auch ansprechen, weil er interessant ist.
In Python 2 verwenden Sie das alte intObjekt, das sich vom intObjekt in Python 3 unterscheidet (alle intObjekte in 3.x sind vom PyLongObjectTyp). In Python 2 gibt es eine Unterscheidung, die vom Wert des Objekts abhängt (oder, wenn Sie das Suffix verwenden L/l):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
Die <type 'int'>Sie hier sehen , macht das gleiche floats tun , wird es sicher in eine C umgewandelt , long wenn Potenzierung auf sie durchgeführt wird (die int_powauch Hinweise der Compiler ‚em in einem Register , wenn es so tun, so dass könnte einen Unterschied machen) ::
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
Dies ermöglicht einen guten Geschwindigkeitsgewinn.
Um zu sehen, wie träge <type 'long'>s im Vergleich zu <type 'int'>s sind, verschwindet der Geschwindigkeitsgewinn , wenn Sie den xNamen in longPython 2 in einen Aufruf eingeschlossen haben (was im Wesentlichen die Verwendung long_powwie in Python 3 erzwingt ):
# <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
Beachten Sie, dass, obwohl das eine Snippet das intzu transformiert , longwährend das andere nicht (wie von @pydsinger hervorgehoben), diese Besetzung nicht die treibende Kraft hinter der Verlangsamung ist. Die Umsetzung von long_powist. (Zeit die Aussagen nur mit long(x)zu sehen).
[...] es passiert nicht außerhalb der Schleife. [...] Irgendeine Idee dazu?
Dies ist CPythons Gucklochoptimierer, der die Konstanten für Sie faltet. In beiden Fällen erhalten Sie genau die gleichen Timings, da keine tatsächliche Berechnung zum Ermitteln des Ergebnisses der Potenzierung erfolgt, sondern nur das Laden von Werten:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
Es wird ein identischer Bytecode generiert, '4 ** 4.'mit dem einzigen Unterschied, dass LOAD_CONSTder Float 256.0anstelle des int geladen wird 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
Die Zeiten sind also identisch.
* Alle oben genannten Punkte gelten ausschließlich für CPython, die Referenzimplementierung von Python. Andere Implementierungen funktionieren möglicherweise anders.