Multi-Core Prozessing

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Montag 24. November 2008, 10:25

Moin, ist es eigentlich möglich ein Python Programm auf mehrere Prozessor Cores zu verteilen?
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Montag 24. November 2008, 10:38

Hi

Schau dir das Modul multiprocessing an, ist seit Python 2.6 dabei. Gibts aber auch als Modul fuer 2.4 und 2.5 pyprocessing

Gruss
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Montag 24. November 2008, 10:41

Oha, danke. Fast ein Argument für Python 2.6. Aber es wird noch nicht alle Bindings dafür geben, oder?
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Montag 24. November 2008, 10:59

Hi

Was fuer Bindings?

Gruss
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Montag 24. November 2008, 11:01

Naja, Bindings zu Toolkits zb. Oder Pygame usw
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Montag 24. November 2008, 11:06

Ein Python-Programm lässt sich auch in 2.6 oder 3.0 nicht leichtgewichtig auf mehreren Prozessorkernen parallel ausführen, da der Global Interpreter Lock (GIL) auch dort dafür sorgt, dass immer nur ein Prozess bzw. Thread zur Zeit im Interpreter läuft, da das ganze Ding aus historischen Gründen nicht multithreading-safe entwickelt ist.

Man kann daher entweder nur I/O auslagern und quasi-parallel im Hintergrund stattfinden lassen oder aber Betriebssystemprozsse, die aber alle wieder das gesamte Python-System laden müssen.

Einfach neue Prozesse zu benutzen, ist ein vergleichsweise teurer Workaround. Selbst Threads können in Fällen, wo man wirklich viele parallele Aufgaben erledigen will, ein zu großer Aufwand sein.

Sagen wir, ein Thread oder Prozess braucht betriebssystemseitig 1 MB bzw. 10 MB RAM. Dann passen in ein 1 GB gerade mal 1000 Threads oder 100 Prozesse. Ein System wie Erlang hantiert mit mehreren 100.000 Prozessen, da diese nicht 1:1 auf Betriebssystem-Ressourcen (Prozesse oder Threads) abgebildet werden.

Python hätte als Interpreter-Sprache die Chance, ebenfalls den ganzen Betriebssystem-Overhead einzusparen, wenn der Interpreter komplett "stackless" entworfen wäre und ähnlich wie Erlang nur wenige hundert Bytes pro Prozess verbrauchen würde und sich die VM um die Abbildung auf das Betriebssystem kümmern würde.

Wenn wir wirklich in Kürze Prozessoren mit 8, 32 oder 64 Kernen in Standard-PCs sehen werden und Hauptspeicher einigermaßen Effizenz genutzt werden soll, sehe ich hier für Python schwarz, den Anschluss zu behalten.

Da bei Java das Multithreading Teil der Sprachspezifikation, niemand aber die VM zwingt, die Java-Threads 1:1 auf Betriebssystemthreads abzubilden, so wie das heutzutage noch passiert, können hier schlaue Leute das Problem einmal vernünftig in der VM lösen und alle Java-Programme werden davon profitieren. Daher setzen viele auf neue JVM-basierte Sprachen wie Scala oder Clojure, die versuchen, eine bessere Abstraktion für Threads und deren Interaktion als "synchronized" und "wait" zu bieten. Scala hat ein Actor-Modell ähnlich wie Erlang und Clojure setzt auf Transactional Memory und unveränderliche Datenstrukturen (die bei Scala empfohlen, aber freiwillig sind).

Übrigens, Jython hat keinen GIL und kann daher von den Threads der JVM (Java Virtuelle Maschine = Ablaufumgebung für Java-Bytecode) profitieren. Als ich mir das das letzte Mal angeschaut hatte, sah das ganze aber recht krude mit thread-lokalen Variablen aus und ich hätte nicht das Vertrauen, das die alle globalen Variablen gefunden und gegeneinander isoliert haben.

Smalltalk hat seit je her leichtgewichtige Prozesse, die aber nie dafür gemacht wurden, parallel zu funktionieren (das daher bräuchte man auch dort den selben GIL wie bei Python) und da hat es AFAIK bislang noch niemand geschafft (vielleicht aber auch nie versucht), ein existierendes System wirklich Threadsafe umzuschreiben. Dave Ungar scheint gerade ein Forschungsprojekt bei IBM durchzuführen, wo man versucht, ein Squeak-Smalltalk, das bereits auf 56 Kernen läuft (allerdings mit GIL) jetzt vorsichtig zu parallelisieren.

Da Python (wenn man noch ein bisschen mehr Cruft über Bord schmeißt als wie Python 3.0 es schon getan hat) eigentlich gar keine so komplizierte Sprache ist, müsste es - so frage ich mich manchmal - doch nicht so schwer sein, einen threadsafen Interpreter von Grund auf neu zu bauen.

Ich habe das mal in Java für Python 1.4 so weit getrieben, bis ich wusste, dass es gehen kann. Dann aber nie das Projekt abgeschlossen. Leider war mein naiver Ansatz eines AST-Interpreters in Java auch vergleichsweise langsam. Etwas wie die DLR von .NET wäre nett als Basis gewesen.

Genug Gefasel :)

Stefan
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Montag 24. November 2008, 11:15

Hi sma,
was bedeutet das letztendlich? Mal abgesehen vom Speicherverbrauch, kann ich zb zwei Prozesse gleichzeitig starten die dann bei einem Dual Core auch auf beiden Kernen parallel laufen? Oder müsste ich quasi zwei Interpreter starten?

Was sollte Multiprocessing bringen wenn es nicht funktioniert?
BlackJack

Montag 24. November 2008, 11:29

Natürlich kannst Du zwei Prozesse starten, das macht `multiprocessing` ja. In jedem Prozess läuft ein eigener Interpreter, muss ja, weil Prozesse so definiert sind, dass sie sich keinen Speicher teilen.

Wie kommst Du jetzt darauf, das `multiprocessing` nicht funktioniert?
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Montag 24. November 2008, 11:36

BlackJack hat geschrieben:Natürlich kannst Wie kommst Du jetzt darauf, das `multiprocessing` nicht funktioniert?
da der Global Interpreter Lock (GIL) auch dort dafür sorgt, dass immer nur ein Prozess bzw. Thread zur Zeit im Interpreter läuft, da das ganze Ding aus historischen Gründen nicht multithreading-safe entwickelt ist.
Der Satz hat mich etwas irritiert. Ich wusste jetzt nicht ob mit einem Prozess automatisch ein neuer Interpreter gestartet wird.

Ich käme natürlich nicht auf die Idee 10000 Prozesse zu starten, aber je nach Anwendung 2-4 Prozesse wären durchaus denkbar
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Montag 24. November 2008, 11:56

burli hat geschrieben:was bedeutet das letztendlich? Mal abgesehen vom Speicherverbrauch, kann ich zb zwei Prozesse gleichzeitig starten die dann bei einem Dual Core auch auf beiden Kernen parallel laufen? Oder müsste ich quasi zwei Interpreter starten?
Ja, zwei Prozesse laufen in der Regel (das Betriebssystem hat da das letzte Wort) parallel auf beiden Kernen. Du startest quasi zwei Interpreter. Ein "leeres" Python verbraucht bei mir ca. 3MB pysikalischen und knapp 600 MB virtuellen Speicher. Lade ich "nevow", sind glatt mal 12 MB pysikalisch weg. Brauchen beide Interpreter eine solche Library, sind es schon 24 MB - das summiert sich schnell.

Ein simples Django-Beispiel braucht 12 MB RAM. Tomcat, als typischer Java-Servlet-Container, hält schon mal 75 Threads vor, um 75 HTTP-Requests quasi-parallel zu bearbeiten. Würde ich 75 nicht-multithreading-fähige Django-Prozesse starten, wären das 900 MB RAM. Da sage noch mal einer, Java wäre speicherhungrig. Ein Django-ähnliches Rahmenwerk käme vielleicht mit 30 MB auf die Waage, aber eben nur einmal in Tomcat, der selbst etwa 30 MB verbraucht.

Das ist der Preis für den GIL.

Stefan
burli
User
Beiträge: 1116
Registriert: Dienstag 9. März 2004, 18:22

Montag 24. November 2008, 12:01

Ich stelle es mir allerdings auch etwas schwierig vor, das ein Interpreter Multiprocessing fähig ist. Der Interpreter selbst müsste sich in mehrere Prozesse aufteilen, aber auf die gleichen Ressourcen zugreifen können um multiple Imports zu vermeiden.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 24. November 2008, 13:12

burli hat geschrieben:Der Interpreter selbst müsste sich in mehrere Prozesse aufteilen, aber auf die gleichen Ressourcen zugreifen können um multiple Imports zu vermeiden.
Tut er ja nicht.

sma, Clojure bietet optional als alternative zu OS-Threads leichtgewichtige Green-Threads, aber das kommt zu dem Preis, dass ein hängender Green-Thread das ganze Programm einfriert. Schon klingt das eher nur mäßig interessant.

Außerdem, obwohl es klar ist, dass Multikerne in Zukunft öfter anzutreffen sein werden, wird sich in Zukunft aber nicht so viel ändern, denn viele Probleme sind nun mal sequentiell, so dass man nicht großartig parallelisieren kann. Programme die heute Threads nutzen werden auch in Zukunft Threads nutzen und da bringt Multicore den Vorteil dass die Threads parallel laufen. Aber viele Programme sind Singlethreaded oder die Threads sind sowieso für IO dar, was auch in Python Parallelisierbar ist. Ich glaube nicht, dass mein Firefox in absehbarer Zeit 100.000 Threads nutzen wird, das ist eher für Server interessant und Applikationen mit großem Datendurchsatz (für die Python sowieso nicht so optimal ist). Klar, GIL ist nicht optimal und multiprocessing ist auch kaum eine optimale Lösung, aber für die nächste Zukunft ist es noch passabel. Die Prozessoren die in User-Rechnern stecken werden sowieso genutzt werden, denn in jedem OS laufen viele Threads parallel.

Für massives Multithreading sind auch die aktuell populären Programmierparadigmen nicht optimal. Daher eben immutable Strukturen und funktionale Programmierung. Erlang, Scala und Clojure versuchen eben diese Lücke zu füllen.

Und einen stacklosen Python Interpreter mit Continuations gibt es ja auch schon. :)
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Montag 24. November 2008, 13:57

Leonidas hat geschrieben:Und einen stacklosen Python Interpreter mit Continuations gibt es ja auch schon. :)
Meinst Du den?
MfG
HWK
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Montag 24. November 2008, 15:01

Die klassische Definition von Prozess und Thread sind diese: Ein Prozess ist ein (gegen andere Prozesse abgesichertes) (nebenläufiges) Programm mit eigenen Betriebssystem-Ressourcen (Speicher, Dateien, Laufzeit). Threads sind nebenläufige Programme in einem Prozess, typischerweise nicht gegeneinander abgesichert.

Das klassische Unix (und Windows hat das dann irgendwann übernommen) betreibt Multiprozessing. Jedem Task sind eigene Betriebssystem-Ressourcen zugewiesen. Bei einem Prozesswechsel müssen daher viele Verwaltungsinformationen gerettet oder umkopiert werden und ein Prozesswechsel dauert (vergleichsweise) lange.

Daher hat man Multithreading als leichtgewichtigere Alternative erfunden. Alle Threads teilen sich die Ressourcen ihres Prozesses.

Den Begriff Task möchte ich für ein (nebenläufig) ablaufenden Programmteil benutzen, das nicht notwendigerweise 1:1 auf Betriebssystemkonzepte abgebildet wird.

Multitasking meint, das mehrere Programmteile (quasi) gleichzeitig ablaufen (können).

Kooperatives Multitasking bedeutet, die Programmteile geben freiwillig die Kontrolle an jeweils den nächsten Task ab. Bei präemptivem Multitasking übernimmt eine zentrale Instanz die Verteilung der Zeitscheiben, also der Möglichkeit, das Programm auf einem physikalischen Prozessor ablaufen lassen zu können.

Kooperatives Multitasking kann man in Python z.B. mit Generatoren (Co-Routinen) realisieren. Jede Art von Batch-Processing oder Verarbeitung von Anfragen ist im Prinzip ein kooperatives Multitasking. Präemptivem Multitasking erreicht man mit der threading-Bibliothek oder durch den Einsatz von präemptiven Betriebssystemprozessen.

Solange man nur einen Prozessor hat, ist Multitasking aber eine Illusion.

Erst wenn mehrere physikalische Prozessoren vorhanden sind, gibt es echte Parallelität. Und damit auch echte Probleme.

Bei einem einzelnen Interpreter von multiprocessing-fähig zu sprechen ist - dieses alles gesagt habend - verwirrend, da er immer nur in einem Betriebssystem-Prozess leben kann, da dies die größte Einheit ist. Mehrere Interpreter-Prozesse können zusammenarbeiten. Oder ein Interpreter kann multithreading-fähig sein.

Korrekt ist, dass z.B. beim Laden neuer Module darauf geachtet werden muss, dass ein multithreading-fähiger Interpreter nicht in einen inkonsistenten Zustand gerät. Am besten, man hat keine globalen und auch noch veränderbaren Ressourcen.

Daher ist das Prozess-Modell so schön einfach. Allerdings bedeutet das auch, dass jeder Prozess seine Module selbst laden muss und das kostet Speicher, den ein multithreading-fähiger Interpreter gemeinsam nutzen könnte.

Bei Unix könnte man das spezielle Verhalten von fork/exec ausnutzen, um hier Speicher zu sparen, doch allgemein gilt das nicht. Hier muss man davon ausgehen, dass jeder Prozess eigenen privaten Speicher braucht.

Ich halte es außerdem für erstrebenswert, auf Sprachebene noch etwas kleineres und leichtgewichtigeres als Betriebssystemthreads zu haben - also etwa Tasks, von denen Zigtausende existieren können. Erlang funktioniert so und nennt diese Tasks dann Prozesse und verschleiert sogar noch vor dem Anwender, ob sie auf einem oder mehreren physikalischen Rechnern ablaufen.

Wie lange es noch dauert, bis der 8-Prozessor-Standard-PC Wirklichkeit wird, weiß ich nicht. Wenn aber jetzt schon 4 Kerne in normalen PCs erschwinglich sind und Spielekonsolen mit 8 oder mehr Kernen daher kommen, passiert das eher heute als morgen.

Das Aufgaben sowieso kaum parallelisiert werden können, würde ich zudem bezweifeln und das auf eine falsche Ausbildung und beschränkende (vielleicht einfach menschliche) Denkweise schieben. Dabei tun wir als Menschen auch viele Dinge gleichzeitig. Ich schreibe dies hier und bewege dabei mehrere Finger gleichzeitig, atme dabei, höre Musik und achte auf die Umgebung und nehme die Autos auf der Straße hinter dem Fenster wahr.

Prinzipiell steckt selbst in so einfachen Ausdrücken wie 3+4 * 5-1 Parallelität, die man auswerten könnte (offensichtlich kann man parallel 3+4 und 5-1 berechnen, bevor man beide Zwischenergebnisse multipliziert).

Ein klassisches Beispiel ist ansonsten natürlich etwas wie Raytracing, wo Millionen von Tasks parallel jeweils die Strahlrückverfolgung für einen Bildpunkt parallel betreiben könnten.

Ein Beispiel eher aus der Praxis dann da vielleicht Webanwendungen, die hunderte, tausende oder hunderttausende von Anfragen parallel bearbeiten sollen. Und diese Art der Anwendungen wird eher mehr als weniger.

Oder Spiele: Habe ich eine Schlachtensimulation mit jeweils 10.000 Rittern pro Seite, dann würde ich erwarten, dass sich ein Task pro Ritter darum kümmert, sich schlau zu verhalten. Ähnlich ging vor einigen Jahren die Software, die benutzt wurde, um die Schlachtszenen im Herr der Ringe zu animieren vor. Jeder Ork hatte ein kleines virtuelles Gehirn, in dem ein kleines Python-Programm ablief.

Klar wird Firefox nicht 100.000 Thread haben, doch jetzt schon hat er bei mir gerade 46 Threads (für was auch immer) und mit dem Ansatz von Chrome, pro Seite und JavaScript einen (gegeneinander isolierten) Thread zu benutzen, wird sowas eher noch steigen.

Und warum sollte ich solche Anwendungen nicht auch in Python entwickeln wollen?

Das Python dafür gerade nicht so optimal ist, liegt doch gerade an dem GIL. Stackless-Python hat doch ebenfalls noch den GIL, oder?

Übrigens, Leonidas, hast du einen Hinweis auf Green-Threads in Clojure? Das wäre mir neu und würde mich ehrlich gesagt wundern, sollte dies doch lieber die JVM regeln. Scala wiederum hat so etwas ähnliches mit den Agents, die alternativ statt Threads auch ein selbstgemachtes Event-System nutzen können, was dann IIRC endrekursive Funktionen benutzt, bei denen von Zeit zu Zeit die Stacks durch gezielt geworfene Exceptions wieder reduziert werden.

Stefan
BlackJack

Montag 24. November 2008, 15:40

@sma: Menschen sind nicht mehr multitaskingfähig als ein Einprozessorsystem. Jetzt mal vom atmen und anderen vegetativen Prozessen abgesehen, die eher von unabhängigen Coprozessoren am Laufen gehalten werden.

Ich finde leider gerade den Link zum Artikel nicht, aber es gab eine Untersuchung wonach bei Leuten, die mehrere Sachen gleichzeitig tun, die verschiedene Gehirnregionen beanspruchen, die einzelnen Regionen immer abwechselnd aktiv, sind. In kurzen Zeitscheiben, wie beim Einprozessorsystem.

Letztlich bringt dieses rumlamentieren über das böse GIL nichts, solange nicht jemand eine neue Python-Implementierung schreibt, die ohne auskommt. Anpassen der aktuellen CPython-Implementierung geht nicht -- das wurde ja schon probiert und das Ergebnis war, das man dann soviel "lock"en muss, dass der Interpreter signifikant langsamer wäre.

Die Rechnung 3+4 * 5-1 kann man zwar parallelisieren, aber da ist dann die Frage, ob sich der Overhead das auf zwei Prozesse zu verteilen lohnt. Zumal das zumindest bei "nativ kompilierten" Sprachen wohl auch bei einem Prozess vom Prozessor schon intern parallelisiert werden kann.
Antworten