globaler und lokaler Namespace-Leistungsunterschied

8

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:

%Vor%     
user119264 11.01.2016, 03:39
quelle

1 Antwort

14

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:

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 :

%Vor%

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:

%Vor%

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 .

    
Jim Fasarakis Hilliard 11.01.2016, 04:40
quelle