Ich verwende Python 2.7.5 auf einem aktuellen Apple MacBook Pro, das vier Hardware- und acht logische CPUs hat; Das Dienstprogramm sysctl gibt Folgendes aus:
%Vor% Ich muss eine ziemlich komplizierte Verarbeitung in einer großen 1-D-Liste oder einem Array durchführen und dann das Ergebnis als eine Zwischenausgabe speichern, die zu einem späteren Zeitpunkt in einer nachfolgenden Berechnung innerhalb meiner Anwendung wieder verwendet wird. Die Struktur meines Problems eignet sich sehr gut zur Parallelisierung, also dachte ich, ich würde versuchen, Pythons Multiprozessor-Modul zu verwenden, um das 1D-Array in mehrere Teile zu unterteilen (entweder 4 oder 8 Stück, ich bin mir noch nicht sicher welche) die Berechnungen parallel und anschließend die resultierende Ausgabe in ihr endgültiges Format wieder zusammen. Ich versuche zu entscheiden, ob multiprocessing.Queue()
(Nachrichtenwarteschlangen) oder multiprocessing.Array()
(gemeinsam genutzter Speicher) als mein bevorzugter Mechanismus für die Übertragung der resultierenden Berechnungen von den Kindprozessen an den Hauptmutterprozess verwendet werden soll, mit denen ich experimentiert habe ein paar "Spielzeug" -Modelle, um sicherzustellen, dass ich verstehe, wie das Multiprocessing-Modul tatsächlich funktioniert. Ich bin jedoch auf ein eher unerwartetes Ergebnis gestoßen: Bei der Erstellung von zwei im Wesentlichen äquivalenten Lösungen für das gleiche Problem scheint die Version, die Shared Memory für die Interprozesskommunikation verwendet, wesentlich mehr Ausführungszeit (30x mehr!) Zu benötigen als die Version mit Message Warteschlangen. Im Folgenden habe ich zwei verschiedene Versionen von Beispielquellcode für ein "Spielzeug" -Problem aufgeführt, das eine lange Folge von Zufallszahlen unter Verwendung paralleler Prozesse erzeugt und das agglomerierte Ergebnis auf zwei verschiedene Arten an einen übergeordneten Prozess zurückgibt: zuerst mithilfe von Nachrichtenwarteschlangen und das zweite Mal Shared Memory verwenden.
Hier ist die Version, die Nachrichtenwarteschlangen verwendet:
%Vor%Wenn ich es ausführe, erhalte ich ein Ergebnis, das normalerweise so aussieht:
%Vor%Nun, hier ist das äquivalente Code-Segment, aber refactored nur leicht, so dass es Shared Memory statt Queues verwendet:
%Vor%Wenn ich die Shared Memory-Version ausführe, sieht das typische Ergebnis jedoch eher so aus:
%Vor%Meine Frage: Warum gibt es einen so großen Unterschied in den Ausführungsgeschwindigkeiten (ungefähr 0,5 Sekunden vs. 15 Sekunden, ein Faktor 30!) zwischen den beiden Versionen meines Codes? Und vor allem, wie kann ich die Shared-Memory-Version ändern, damit sie schneller läuft?
Dies liegt daran, dass multiprocessing.Array
standardmäßig eine Sperre verwendet, um zu verhindern, dass mehrere Prozesse gleichzeitig darauf zugreifen:
multiprocessing.Array (typecode_or_type, size_or_initializer, *, Sperre = True)
...
Wenn die Sperre auf "True" (die Standardeinstellung) gesetzt ist, wird ein neues Sperrobjekt erstellt, um den Zugriff auf den Wert zu synchronisieren. Wenn die Sperre ein Lock- oder RLock-Objekt ist Dann wird der Synchronisierungszugriff auf den Wert verwendet. Wenn das Schloss ist False, dann wird der Zugriff auf das zurückgegebene Objekt nicht automatisch erfolgen geschützt durch ein Schloss, so dass es nicht unbedingt "prozesssicher" ist.
Das bedeutet, dass Sie nicht gleichzeitig in das Array schreiben - nur ein Prozess kann gleichzeitig darauf zugreifen. Da Ihre Beispiel-Worker fast nichts anderes tun als Array-Schreibzugriffe, stört das ständige Warten auf diese Sperre die Leistung. Wenn Sie lock=False
verwenden, wenn Sie das Array erstellen, ist die Leistung viel besser:
Mit lock=True
:
Mit lock=False
:
Beachten Sie, dass die Verwendung von lock=False
bedeutet, dass Sie den Zugriff auf Array
manuell schützen müssen, wenn Sie etwas tun, das nicht prozesssicher ist. Ihr Beispiel besteht darin, Prozesse in eindeutige Teile schreiben zu lassen, also ist es in Ordnung. Wenn Sie jedoch dabei versuchen würden, davon zu lesen oder verschiedene Prozesse in überlappende Teile schreiben würden, müssten Sie eine Sperre manuell erfassen.
Tags und Links python performance shared-memory message-queue multiprocessing