Warum benötigt meine Schleife bei jeder Iteration mehr Speicher?

8

Ich versuche, die Speicheranforderungen meines Python 3-Codes zu reduzieren. Jede Iteration der for-Schleife benötigt jetzt mehr Speicher als der letzte.

Ich habe ein kleines Stück Code geschrieben, das dasselbe Verhalten wie mein Projekt hat:

%Vor%

Für jede Iteration der for-Schleife haben die Threads in simulation () jeweils eine Speicherbelegung, die dem gesamten von meinem Code verwendeten Speicher entspricht.

Kloniert Python bei jeder Ausführung der parallelen Prozesse meine gesamte Umgebung, einschließlich der Variablen, die f () nicht benötigt? Wie kann ich dieses Verhalten verhindern?

Idealerweise möchte ich, dass mein Code nur den Speicher kopiert, den er zum Ausführen von f () benötigt, während ich die Ergebnisse im Speicher ablegen kann.

    
Isea 17.03.2016, 17:17
quelle

2 Antworten

2

Obwohl das Skript selbst bei den "kleineren" Beispielwerten ziemlich viel Speicher verwendet, lautet die Antwort auf

  

Klont Python bei jeder Parallele meine gesamte Umgebung   Prozesse werden ausgeführt, einschließlich der Variablen, die nicht von f () benötigt werden? Wie   kann ich dieses Verhalten verhindern?

ist, dass es die Umgebung mit Forking klonen ein neuer Prozess, aber wenn copy-on-write Semantik verfügbar ist, muss kein tatsächlicher physikalischer Speicher vorhanden sein kopiert, bis es geschrieben ist. Zum Beispiel auf diesem System

%Vor%

COW scheint verfügbar und in Verwendung zu sein, dies ist jedoch auf anderen Systemen möglicherweise nicht der Fall. Unter Windows ist dies streng anders, da ein neuer Python-Interpreter von .exe anstelle von Forking ausgeführt wird. Da Sie die Verwendung von htop erwähnen, verwenden Sie ein UNIX- oder UNIX-ähnliches System, und Sie erhalten COW -Semantik.

  

Für jede Iteration der for-Schleife werden die -Prozesse in simulation () durchgeführt   habe eine Speicherbelegung, die dem gesamten von meinem Code verwendeten Speicher entspricht.

Die erzeugten Prozesse werden fast identische Werte von RSS anzeigen, aber das kann irreführend sein, weil sie meistens besetzt sind derselbe tatsächliche physische Speicher, der mehreren Prozessen zugeordnet ist, wenn keine Schreibvorgänge auftreten. Mit Pool.map ist die Geschichte ein bisschen komplizierter, als es "chops das iterable in eine Anzahl von Chunks, die es als separate Tasks an den Prozesspool sendet". Diese Übermittlung erfolgt über IPC und die übermittelten Daten werden kopiert. In Ihrem Beispiel dominieren auch die Funktionsaufrufe IPC und 2 ** 20 die CPU-Auslastung. Das Ersetzen des Mappings durch eine einzelne vektorisierte Multiplikation in simulation hat die Laufzeit des Skripts von ca. 150s auf 0.66s auf diesem Rechner gebracht.

Wir können COW mit einem (etwas) vereinfachten Beispiel beobachten, das ein großes Array zuordnet und es an einen erzeugten Prozess zur schreibgeschützten Verarbeitung übergibt:

%Vor%

Ausgabe auf dieser Maschine:

%Vor%

Wenn wir nun die Funktion read_arr durch eine Funktion ersetzen, die das Array verändert:

%Vor%

Die Ergebnisse sind ziemlich unterschiedlich:

%Vor%

Die for-Schleife benötigt zwar nach jeder Iteration mehr Speicher, aber das ist offensichtlich: Sie stapelt total_results aus dem Mapping und muss daher Platz für ein neues Array reservieren, das sowohl die alten als auch die neuen und enthält befreie das nun unbenutzte Array alter Ergebnisse.

    
Ilja Everilä 18.03.2016, 06:53
quelle
0

Vielleicht sollten Sie den Unterschied zwischen thread und process in Operating System kennen. Siehe hierzu Was ist der Unterschied zwischen einem Prozess und einem Thread? .

In der for-Schleife gibt es processes , nicht threads . Threads teilen sich den Adressraum des Prozesses, der sie erstellt hat; Prozesse haben ihren eigenen Adressraum.

Sie können die Prozess-ID drucken, indem Sie os.getpid() eingeben.

    
Alexander Yau 18.03.2016 07:10
quelle