Warum führt eine Reihe von Befehlen in einer Funktion aus:
%Vor% tendiert dazu, in Python 1.5x
bis 3x
mal schneller auszuführen als Befehle auf der obersten Ebene auszuführen:
Der Unterschied ist tatsächlich stark hängt davon ab, was "do stuff" eigentlich macht und hauptsächlich wie oft er auf Namen zugreift, die definiert / verwendet werden. Zugegeben, dass der Code ähnlich ist, gibt es einen grundlegenden Unterschied zwischen diesen beiden Fällen:
LOAD_FAST
. STORE_FAST
/ LOAD_NAME
die träger sind. Dies kann in den folgenden Fällen angezeigt werden, Ich werde eine STORE_NAME
-Schleife verwenden, um sicherzustellen, dass Nachschlagevorgänge für definierte Variablen mehrmals ausgeführt werden .
Funktion und for
:
Wir definieren eine einfache Funktion, die einige wirklich dumme Sachen macht:
%Vor% Ausgabe generiert von LOAD_FAST/STORE_FAST
:
Zu beachten sind hier die dis.dis
-Befehle an den Offsets LOAD_FAST/STORE_FAST
und 28
, diese werden verwendet, um auf den 32
name in der b
-Operation zuzugreifen und den BINARY_MULTIPLY
name zu speichern. beziehungsweise. Wie ihr Bytecodename impliziert, sind sie die schnelle Version der z
-Familie.
Module und LOAD_*/STORE_*
:
Sehen wir uns nun die Ausgabe von LOAD_NAME/STORE_NAME
für unsere Modulversion der vorherigen Funktion an:
Hier haben wir mehrere Aufrufe von dis
, welche , wie bereits erwähnt, träge Befehle ausführen .
In diesem Fall gibt es einen deutlichen Unterschied in der Ausführungszeit , hauptsächlich weil Python LOAD_NAME/STORE_NAME
und LOAD_NAME/STORE_NAME
mehrere Male auswerten muss (aufgrund der hinzugefügten LOAD_FAST/STORE_FAST
Schleife) ) und infolgedessen der Overhead, der jedes Mal eingeführt wird, wenn der Code für jeden Bytecode ausgeführt wird akkumuliert .
Timing der Ausführung 'als Modul':
%Vor%Timing der Ausführung als Funktion:
%Vor% Wenn Sie for
in einem kleineren time
durchlaufen (zum Beispiel range
), werden Sie feststellen, dass die 'Modul' Version schneller ist. Dies geschieht, weil der Overhead, der durch das Aufrufen der Funktion for i in range(1000)
eingeführt wird, größer ist als der durch main()
vs *_FAST
Unterschiede eingeführte Overhead. Es ist also weitgehend relativ zu der Menge an Arbeit, die getan wird.
Also, der wahre Schuldige hier, und der Grund, warum dieser Unterschied offensichtlich ist, ist die verwendete *_NAME
-Schleife. In der Regel haben Sie for
Grund, jemals eine solche intensive Schleife auf die oberste Ebene Ihres Skripts zu setzen. Verschieben Sie es in einer Funktion und vermeiden Sie die Verwendung von globalen Variablen , es ist entworfen, um effizienter zu sein.
Sie können sich den Code ansehen, der für jeden Bytecode ausgeführt wird. Ich verlinke hier die Quelle für die 0
Version von Python, obwohl ich ziemlich sicher bin 3.5
unterscheidet sich nicht sehr. Die Bytecode-Auswertung erfolgt in 2.7
speziell in der Funktion Python/ceval.c
:
Wie Sie sehen werden, erhalten die STORE_NAME source
bytecodes einfach den Wert gespeichert / geladen mit einem *_FAST
lokale Symboltabelle innerhalb von Rahmenobjekten .
Tags und Links python optimization python-3.x python-2.7