Ich möchte mein Python-Programm parallelisieren, damit es mehrere Prozessoren auf dem Computer verwenden kann, auf dem es ausgeführt wird. Meine Parallelisierung ist sehr einfach, da alle parallelen "Threads" des Programms unabhängig sind und ihre Ausgabe in separate Dateien schreiben. Ich brauche die Threads nicht, um Informationen auszutauschen, aber ich muss unbedingt wissen, wann die Threads fertig sind, da einige Schritte meiner Pipeline von ihrer Ausgabe abhängen.
Portabilität ist wichtig, da ich möchte, dass dies auf jeder Python-Version unter Mac, Linux und Windows ausgeführt wird. Welches Python-Modul ist angesichts dieser Einschränkungen am besten geeignet, um dies zu implementieren? Ich versuche, mich zwischen Thread, Unterprozess und Multiprocessing zu entscheiden, die alle verwandte Funktionen bieten.
Irgendwelche Gedanken dazu? Ich möchte die einfachste Lösung, die portabel ist.
quelle
Antworten:
multiprocessing
ist ein großartiges Modul für Schweizer Taschenmesser. Es ist allgemeiner als Threads, da Sie sogar Remote-Berechnungen durchführen können. Dies ist daher das Modul, das ich Ihnen empfehlen würde.Mit dem
subprocess
Modul können Sie auch mehrere Prozesse starten, aber ich fand es weniger bequem als das neue Multiprozessor-Modul.Threads sind notorisch subtil, und mit CPython sind Sie häufig auf einen Kern beschränkt (obwohl, wie in einem der Kommentare erwähnt, die globale Interpreter-Sperre (GIL) in C-Code freigegeben werden kann, der aus Python-Code aufgerufen wird). .
Ich glaube, dass die meisten Funktionen der drei von Ihnen genannten Module plattformunabhängig genutzt werden können. Beachten Sie auf der Portabilitätsseite, dass dies
multiprocessing
erst seit Python 2.6 Standard ist (eine Version für einige ältere Versionen von Python gibt es jedoch). Aber es ist ein großartiges Modul!quelle
Für mich ist das eigentlich ganz einfach:
Die Unterprozessoption :
subprocess
dient zum Ausführen anderer ausführbarer Dateien --- es handelt sich im Grunde genommen um einen Wrapperos.fork()
undos.execve()
mit Unterstützung für optionale Installationen (Einrichten von PIPEs zu und von den Unterprozessen. Natürlich können Sie auch andere IPC-Mechanismen (Inter-Process Communications) wie Sockets oder Posix oder verwenden Gemeinsamer SysV-Speicher. Sie sind jedoch auf die Schnittstellen und IPC-Kanäle beschränkt, die von den von Ihnen aufgerufenen Programmen unterstützt werden.In der Regel wird jedes
subprocess
synchron verwendet - einfach ein externes Dienstprogramm aufrufen und dessen Ausgabe zurücklesen oder auf dessen Fertigstellung warten (möglicherweise die Ergebnisse aus einer temporären Datei lesen oder nachdem sie in einer Datenbank veröffentlicht wurden).Man kann jedoch Hunderte von Unterprozessen erzeugen und abfragen. Mein persönlicher Lieblings-Utility- Classh macht genau das. Der größte Nachteil des
subprocess
Moduls besteht darin, dass die E / A-Unterstützung im Allgemeinen blockiert. Es gibt einen Entwurf für PEP-3145 , um dies in einer zukünftigen Version von Python 3.x zu beheben, und einen alternativen Asyncproc (Warnung, die direkt zum Download führt, nicht zu irgendeiner Art von Dokumentation oder README). Ich habe auch festgestellt, dass es relativ einfach ist,fcntl
IhrePopen
PIPE-Dateideskriptoren direkt zu importieren und zu bearbeiten - obwohl ich nicht weiß, ob dies auf Nicht-UNIX-Plattformen portierbar ist.(Update: 7. August 2019: Python 3-Unterstützung für Ayncio-Unterprozesse: Asyncio-Unterprozesse )
subprocess
hat fast keine Unterstützung für die Ereignisbehandlung ... obwohl Sie dassignal
Modul und einfache UNIX / Linux-Signale der alten Schule verwenden können - und Ihre Prozesse sozusagen sanft beenden.Die Mehrfachverarbeitungsoption :
multiprocessing
dient zum Ausführen von Funktionen in Ihrem vorhandenen (Python-) Code mit Unterstützung für eine flexiblere Kommunikation zwischen dieser Prozessfamilie. Insbesondere ist es am besten, Ihrenmultiprocessing
IPC nachQueue
Möglichkeit um die Objekte des Moduls herum zu erstellen. Sie können jedoch auchEvent
Objekte und verschiedene andere Funktionen verwenden (von denen einige vermutlichmmap
auf der Unterstützung auf Plattformen basieren, auf denen diese Unterstützung ausreicht).Das Python-
multiprocessing
Modul soll Schnittstellen und Funktionen bereitstellen ,threading
die CPython sehr ähnlich sind, während CPython Ihre Verarbeitung trotz GIL (Global Interpreter Lock) auf mehrere CPUs / Kerne skalieren kann. Es nutzt alle fein abgestimmten SMP-Sperr- und Kohärenzanstrengungen, die von Entwicklern Ihres Betriebssystemkerns durchgeführt wurden.Die Threading- Option:
threading
ist für einen relativ engen Bereich von Anwendungen gedacht, die E / A-gebunden sind (nicht über mehrere CPU-Kerne skaliert werden müssen) und die von der extrem geringen Latenz und dem Switching-Overhead beim Thread-Switching (mit gemeinsam genutztem Kernspeicher) im Vergleich zum Prozess / profitieren. Kontextwechsel. Unter Linux ist dies fast die leere Menge (Linux-Prozesswechselzeiten liegen extrem nahe an den Thread-Schaltern).threading
leidet an zwei großen Nachteilen in Python .Eine davon ist natürlich implementierungsspezifisch - betrifft hauptsächlich CPython. Das ist die GIL. Zum größten Teil profitieren die meisten CPython-Programme nicht von der Verfügbarkeit von mehr als zwei CPUs (Kernen), und häufig leidet die Leistung unter dem GIL-Sperrkonflikt.
Das größere Problem, das nicht implementierungsspezifisch ist, besteht darin, dass Threads denselben Speicher, dieselben Signalhandler, Dateideskriptoren und bestimmte andere Betriebssystemressourcen verwenden. Daher muss der Programmierer äußerst vorsichtig sein, wenn Objekte gesperrt, Ausnahmen behandelt und andere Aspekte ihres Codes behandelt werden, die sowohl subtil sind als auch den gesamten Prozess (eine Reihe von Threads) beenden, blockieren oder blockieren können.
Im Vergleich dazu
multiprocessing
gibt das Modell jedem Prozess seinen eigenen Speicher, Dateideskriptoren usw. Ein Absturz oder eine nicht behandelte Ausnahme in einem von ihnen tötet nur diese Ressource, und die robuste Behandlung des Verschwindens eines untergeordneten Prozesses oder eines Geschwisterprozesses kann erheblich einfacher sein als das Debuggen und Isolieren und Beheben oder Umgehen ähnlicher Probleme in Threads.threading
mit wichtigen Python-Systemen wie NumPy kann erheblich weniger unter GIL-Konflikten leiden als die meisten Ihrer eigenen Python-Codes. Dies liegt daran, dass sie speziell dafür entwickelt wurden. Die nativen / binären Teile von NumPy, Gibt beispielsweise die GIL frei, wenn dies sicher ist.Die verdrehte Option:
Es ist auch erwähnenswert, dass Twisted eine weitere Alternative bietet, die sowohl elegant als auch sehr schwierig zu verstehen ist . Grundsätzlich bietet Twisted ein ereignisgesteuertes kooperatives Multitasking innerhalb eines (einzelnen) Prozesses, da die Gefahr besteht, dass es zu stark vereinfacht wird, bis Fans von Twisted mein Zuhause mit Heugabeln und Fackeln stürmen.
Um zu verstehen, wie dies möglich ist, sollte man sich über die Funktionen von
select()
(die auf select () oder poll () oder ähnlichen Betriebssystemaufrufen basieren können) informieren. Grundsätzlich hängt alles von der Möglichkeit ab, eine Anforderung an das Betriebssystem zu stellen, bis eine Aktivität in einer Liste von Dateideskriptoren oder eine Zeitüberschreitung vorliegt.Das Erwachen aus jedem dieser Aufrufe von
select()
ist ein Ereignis - entweder ein Ereignis, bei dem Eingaben für eine bestimmte Anzahl von Sockets oder Dateideskriptoren verfügbar (lesbar) sind, oder Pufferplatz für andere (beschreibbare) Deskriptoren oder Sockets verfügbar wird, einige außergewöhnliche Bedingungen (TCP) Out-of-Band-PUSH-Pakete (z. B.) oder ein TIMEOUT.Daher basiert das Twisted-Programmiermodell darauf, diese Ereignisse zu behandeln und dann den resultierenden "Haupt" -Handler zu durchlaufen, sodass er die Ereignisse an Ihre Handler senden kann.
Ich persönlich denke an den Namen Twisted als Anspielung auf das Programmiermodell ... da Ihre Herangehensweise an das Problem in gewissem Sinne von innen nach außen "verdreht" sein muss. Anstatt Ihr Programm als eine Reihe von Operationen für Eingabedaten und Ausgaben oder Ergebnisse zu verstehen, schreiben Sie Ihr Programm als Dienst oder Dämon und definieren, wie es auf verschiedene Ereignisse reagiert. (Tatsächlich ist die Kern- "Hauptschleife" eines Twisted-Programms (normalerweise? Immer?) A
reactor()
).Die größten Herausforderungen bei der Verwendung von Twisted bestehen darin, sich mit dem ereignisgesteuerten Modell auseinanderzusetzen und die Verwendung von Klassenbibliotheken oder Toolkits zu vermeiden, die nicht für die Zusammenarbeit innerhalb des Twisted-Frameworks geschrieben wurden. Aus diesem Grund bietet Twisted eigene Module für die Verarbeitung von SSH-Protokollen, für Flüche und eigene Unterprozess- / Popen-Funktionen sowie viele andere Module und Protokollhandler an, die auf den ersten Blick die Dinge in den Python-Standardbibliotheken zu duplizieren scheinen.
Ich denke, es ist nützlich, Twisted auf konzeptioneller Ebene zu verstehen, auch wenn Sie nie beabsichtigen, es zu verwenden. Es kann Einblicke in die Leistung, die Konkurrenz und die Ereignisbehandlung in Ihrem Threading, in der Mehrfachverarbeitung und sogar in der Verarbeitung von Unterprozessen sowie in jede verteilte Verarbeitung geben, die Sie durchführen.
( Hinweis: Neuere Versionen von Python 3.x enthalten Asyncio-Funktionen (asynchrone E / A) wie z async def , den @ async.coroutine- Dekorator und das Schlüsselwort await und ergeben sich aus der zukünftigen Unterstützung. Alle diese Funktionen ähneln in etwa denen Aus Prozessperspektive (kooperatives Multitasking) verdreht ). (Den aktuellen Status der Twisted-Unterstützung für Python 3 finden Sie unter: https://twistedmatrix.com/documents/current/core/howto/python3.html )
Die verteilte Option:
Ein weiterer Bereich der Verarbeitung, nach dem Sie nicht gefragt haben, der jedoch eine Überlegung wert ist, ist der der verteilten Verarbeitung. Es gibt viele Python-Tools und Frameworks für die verteilte Verarbeitung und parallele Berechnung. Persönlich denke ich, dass die am einfachsten zu verwendende eine ist, die am seltensten als in diesem Raum befindlich angesehen wird.
Es ist fast trivial, eine verteilte Verarbeitung um Redis herum aufzubauen . Der gesamte Schlüsselspeicher kann zum Speichern von Arbeitseinheiten und Ergebnissen verwendet werden, Redis-LISTs können als gleiches
Queue()
Objekt verwendet werden und die PUB / SUB-Unterstützung kann für eineEvent
ähnliche Behandlung verwendet werden. Sie können Ihre Schlüssel hashen und Werte verwenden, die über einen losen Cluster von Redis-Instanzen repliziert wurden, um die Topologie und Hash-Token-Zuordnungen zu speichern, um konsistentes Hashing und Failover für die Skalierung bereitzustellen, die über die Kapazität einer einzelnen Instanz zur Koordinierung Ihrer Mitarbeiter hinausgeht und Marshalling-Daten (eingelegt, JSON, BSON oder YAML) unter ihnen.Wenn Sie mit dem Aufbau einer größeren und komplexeren Lösung für Redis beginnen, implementieren Sie natürlich viele der Funktionen, die bereits mit Celery , Apache Spark und Hadoop gelöst wurden. Zoowärter , ETCD , Cassandra und so weiter. Diese haben alle Module für den Python-Zugriff auf ihre Dienste.
[Update: Einige Ressourcen sollten berücksichtigt werden, wenn Sie Python für rechenintensiv auf verteilten Systemen in Betracht ziehen: IPython Parallel und PySpark . Während dies verteilte Allzweck-Computersysteme sind, sind sie besonders zugängliche und beliebte Subsysteme (Data Science and Analytics).
Fazit
Dort haben Sie die Möglichkeit, Alternativen für Python zu verarbeiten, von Single-Threaded mit einfachen synchronen Aufrufen zu Subprozessen, Pools abgefragter Subprozesse, Threaded- und Multiprocessing, ereignisgesteuertem kooperativem Multitasking bis hin zur verteilten Verarbeitung.
quelle
In einem ähnlichen Fall entschied ich mich für separate Prozesse und die wenig notwendige Kommunikation über den Netzwerk-Socket. Es ist sehr portabel und recht einfach mit Python zu tun, aber wahrscheinlich nicht einfacher (in meinem Fall hatte ich auch eine andere Einschränkung: die Kommunikation mit anderen in C ++ geschriebenen Prozessen).
In Ihrem Fall würde ich mich wahrscheinlich für Multiprozess entscheiden, da Python-Threads, zumindest bei Verwendung von CPython, keine echten Threads sind. Nun, es handelt sich um native Systemthreads, aber von Python aufgerufene C-Module können die GIL freigeben oder nicht und anderen Threads ermöglichen, dass sie beim Aufrufen von Blockierungscode ausgeführt werden.
quelle
Um mehrere Prozessoren in CPython zu verwenden, haben Sie nur das
multiprocessing
Modul zur Auswahl . CPython sperrt seine Interna (die GIL ), wodurch verhindert wird, dass Threads auf anderen CPUs parallel arbeiten. Dasmultiprocessing
Modul erstellt neue Prozesse (wiesubprocess
) und verwaltet die Kommunikation zwischen ihnen.quelle
Shell out und lassen Sie das Unix raus, um Ihre Arbeit zu erledigen:
Verwenden Sie Iterpipes , um den Unterprozess zu verpacken, und dann:
Von Ted Ziubas Seite
INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM parallele Prozesse
ODER
Gnu Parallel wird auch dienen
Sie hängen mit GIL ab, während Sie die Jungs im Hinterzimmer losschicken, um Ihre Multicore-Arbeit zu erledigen.
quelle