Warum ist x**4.0
schneller als x**4
? Ich verwende 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
Ich habe versucht, die Kraft, die ich erhöht habe, zu ändern, um zu sehen, wie sie sich verhält. Wenn ich beispielsweise x auf 10 oder 16 erhöhe, springt sie von 30 auf 35, aber wenn ich als Schwimmer um 10,0 erhöhe, bewegt sie sich nur um 24.1 ~ 4.
Ich denke, es hat etwas mit Float-Konvertierung und Potenzen von 2 zu tun, aber ich weiß es nicht wirklich.
Ich habe festgestellt, dass in beiden Fällen Potenzen von 2 schneller sind, da diese Berechnungen für den Dolmetscher / Computer nativer / einfacher sind. Trotzdem bewegt es sich mit Schwimmern fast nicht. 2.0 => 24.1~4 & 128.0 => 24.1~4
aber 2 => 29 & 128 => 62
TigerhawkT3 wies darauf hin, dass dies nicht außerhalb der Schleife geschieht. Ich habe nachgesehen und die Situation tritt nur auf (nach dem, was ich gesehen habe), wenn die Basis angehoben wird. Irgendeine Idee dazu?
python
performance
python-3.x
python-3.5
python-internals
Arieljannai
quelle
quelle
x**4.0
und 3,9 fürx**4
.Antworten:
Python 3-
int
Objekte 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 alsPyLongObject *
Typ-In deklariert werdenlong_pow
). Dies macht ihre Potenzierung auch viel schwieriger und langwieriger, da Sie mit demob_digit
Array 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 aufPyLongObject
. S)float
Im Gegensatz dazu können Python- Objektedouble
(mithilfe vonPyFloat_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 istpow
, das ist ) , um die tatsächliche Potenzierung zu handhaben :wo
iv
undiw
sind unsere originalenPyFloatObject
s als Cdouble
s.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
int
Objekt, das sich vomint
Objekt in Python 3 unterscheidet (alleint
Objekte in 3.x sind vomPyLongObject
Typ). In Python 2 gibt es eine Unterscheidung, die vom Wert des Objekts abhängt (oder, wenn Sie das Suffix verwendenL/l
):Die
<type 'int'>
Sie hier sehen , macht das gleichefloat
s tun , wird es sicher in eine C umgewandelt ,long
wenn Potenzierung auf sie durchgeführt wird (dieint_pow
auch Hinweise der Compiler ‚em in einem Register , wenn es so tun, so dass könnte einen Unterschied machen) ::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 denx
Namen inlong
Python 2 in einen Aufruf eingeschlossen haben (was im Wesentlichen die Verwendunglong_pow
wie in Python 3 erzwingt ):Beachten Sie, dass, obwohl das eine Snippet das
int
zu transformiert ,long
während das andere nicht (wie von @pydsinger hervorgehoben), diese Besetzung nicht die treibende Kraft hinter der Verlangsamung ist. Die Umsetzung vonlong_pow
ist. (Zeit die Aussagen nur mitlong(x)
zu sehen).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:
Es wird ein identischer Bytecode generiert,
'4 ** 4.'
mit dem einzigen Unterschied, dassLOAD_CONST
der Float256.0
anstelle des int geladen wird256
: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.
quelle
range
, da nur die**
Operation selbst keinen Unterschied zwischen ganzen Zahlen und Gleitkommazahlen ergibt.4**4
ist genauso schnell wie4**4.0
), und diese Antwort berührt das überhaupt nicht.dis(compile('4 ** 4', '', 'exec'))
) gefaltet, sodass die Zeit genau gleich sein sollte.long(x)**2.
ist immer noch schneller alslong(x)**2
um den Faktor 4-5. (Nicht einer der Downvoter)<type 'long'>
Typs in Python 3 erklärt sich wahrscheinlich aus den Bemühungen, die Sprache zu vereinfachen. Wenn Sie einen Typ zur Darstellung von Ganzzahlen haben können, ist dieser übersichtlicher als zwei (und Sie müssen sich bei Bedarf Gedanken über die Konvertierung von einem in den anderen machen, Benutzer werden verwirrt usw.). Die Geschwindigkeitsverstärkung ist zweitrangig. Der Begründungsteil von PEP 237 bietet auch weitere Einblicke.Wenn wir uns den Bytecode ansehen, können wir sehen, dass die Ausdrücke rein identisch sind. Der einzige Unterschied ist der Typ einer Konstante, für die ein Argument vorliegt
BINARY_POWER
. Es liegt also mit Sicherheit daran,int
dass auf der ganzen Linie in eine Gleitkommazahl konvertiert wird.Update: Schauen wir uns Objects / abstract.c im CPython-Quellcode an:
PyNumber_Power
Anrufeternary_op
, die zu lang sind, um sie hier einzufügen . Hier ist der Link .Es nennt den
nb_power
Slot vonx
,y
als Argument übergeben.Schließlich sehen wir in
float_pow()
Zeile 686 von Objects / floatobject.c , dass Argumentedouble
unmittelbar vor der eigentlichen Operation in ein C konvertiert werden:quelle
float_pow
wenn das nicht einmal für den langsamen Fall läuft?4**4
und4**4.0
konstant gefaltet werden. Das ist ein völlig separater Effekt.Weil einer richtig ist, ist ein anderer Annäherung.
quelle