Auf welche CPU-Kerne sind meine Python-Prozesse läuft?

Die Einrichtung

Ich habe eine ziemlich komplexe Software in Python (auf einem Windows PC) geschrieben. Meine Software startet grundsätzlich zwei Python-Interpreter-Shells. Die erste Shell startet (ich vermute), wenn du auf die main.py Datei main.py . Innerhalb dieser Schale werden andere Fäden wie folgt gestartet:

  # Start TCP_thread TCP_thread = threading.Thread(name = 'TCP_loop', target = TCP_loop, args = (TCPsock,)) TCP_thread.start() # Start UDP_thread UDP_thread = threading.Thread(name = 'UDP_loop', target = UDP_loop, args = (UDPsock,)) TCP_thread.start() 

Der Main_thread startet ein TCP_thread und ein UDP_thread . Obwohl diese separaten Threads sind, laufen sie alle innerhalb einer einzigen Python-Shell.

Der Main_thread startet auch einen Teilprozess. Dies geschieht auf folgende Weise:

 p = subprocess.Popen(['python', mySubprocessPath], shell=True) 

Aus der Python-Dokumentation verstehe ich, dass dieser Teilprozess gleichzeitig (!) In einer separaten Python-Interpreter-Session / Shell läuft. Das Main_thread in diesem Main_thread ist ganz meiner GUI gewidmet. Die GUI startet ein TCP_thread für alle ihre Kommunikation.

Ich weiß, dass die Dinge etwas kompliziert werden. Deshalb habe ich das ganze Setup in dieser Figur zusammengefasst:

Bildbeschreibung hier eingeben


Ich habe mehrere Fragen zu diesem Setup. Ich werde sie hier auflisten:

Frage 1 [ gelöst ]

Ist es wahr, dass ein Python-Interpreter nur einen CPU-Kern verwendet, um alle Threads zu laufen? Mit anderen Worten, wird die Python interpreter session 1 (aus der Figur) alle 3 Threads ( Main_thread , TCP_thread und UDP_thread ) auf einem CPU-Core UDP_thread ?

Antwort: Ja, das ist wahr. Die GIL (Global Interpreter Lock) sorgt dafür, dass alle Threads gleichzeitig auf einem CPU-Core laufen.

Frage 2 [ Noch nicht gelöst ]

Habe ich einen Weg, um zu verfolgen, welcher CPU-Kern es ist?

Frage 3 [ Teilweise gelöst ]

Für diese Frage vergessen wir über Threads , aber wir konzentrieren uns auf den Subprozess- Mechanismus in Python. Das Starten eines neuen Teilprozesses impliziert das Starten einer neuen Python-Interpreterinstanz. Ist das richtig?

Antwort: Ja das ist richtig. Zuerst gab es einige Verwirrung darüber, ob der folgende Code eine neue Python-Interpreter-Instanz erstellen würde:

  p = subprocess.Popen(['python', mySubprocessPath], shell = True) 

Die Frage wurde geklärt. Dieser Code startet tatsächlich eine neue Python-Interpreter-Instanz.

Ist Python schlau genug, um diese separate Python-Interpreter-Instanz auf einem anderen CPU-Kern laufen zu lassen? Gibt es einen Weg, um zu verfolgen, welche, vielleicht auch mit einigen sporadischen Druckaussagen?

Frage 4 [ Neue Frage ]

Die Gemeinschaftsdiskussion hat eine neue Frage gestellt. Es gibt anscheinend zwei Ansätze beim Laichen eines neuen Prozesses (innerhalb einer neuen Python-Interpreter-Instanz):

  # Approach 1(a) p = subprocess.Popen(['python', mySubprocessPath], shell = True) # Approach 1(b) (JF Sebastian) p = subprocess.Popen([sys.executable, mySubprocessPath]) # Approach 2 p = multiprocessing.Process(target=foo, args=(q,)) 

Der zweite Ansatz hat den offensichtlichen Nachteil, dass es nur eine Funktion zielt – während ich ein neues Python-Skript eröffnen muss. Wie auch immer, sind beide Ansätze ähnlich in dem, was sie erreichen?

3 Solutions collect form web for “Auf welche CPU-Kerne sind meine Python-Prozesse läuft?”

F: Ist es wahr, dass ein Python-Interpreter nur einen CPU-Kern verwendet, um alle Threads zu laufen?

Nr. GIL und CPU-Affinität sind nicht zusammenhängende Konzepte. GIL kann bei der Blockierung von I / O-Operationen freigegeben werden, lange CPU-intensive Berechnungen in einer C-Erweiterung trotzdem.

Wenn ein Faden auf GIL blockiert ist; Es ist wahrscheinlich nicht auf jedem CPU-Kern und daher ist es fair zu sagen, dass reine Python Multithreading-Code kann nur eine CPU-Kern zu einem Zeitpunkt auf CPython-Implementierung verwenden.

F: Mit anderen Worten: Wird die Python-Interpreter-Session 1 (aus der Figur) alle 3 Threads (Main_thread, TCP_thread und UDP_thread) auf einem CPU-Core ausführen?

Ich glaube nicht, dass CPython CPU-Affinität implizit verwaltet. Es ist wahrscheinlich stützt sich auf OS Scheduler zu wählen, wo ein Thread laufen. Python-Threads sind auf echten OS-Threads implementiert.

Q: Oder ist der Python-Interpreter in der Lage, sie über mehrere Kerne zu verbreiten?

Um die Anzahl der nutzbaren CPUs herauszufinden:

 >>> import os >>> len(os.sched_getaffinity(0)) 16 

Wieder, ob Threads auf verschiedenen CPUs geplant sind oder nicht, hängt nicht von Python-Interpreter ab.

Q: Angenommen, die Antwort auf Frage 1 ist "Mehrfachkerne", habe ich einen Weg, um zu verfolgen, auf welchem ​​Kern jeder Thread läuft, vielleicht mit einigen sporadischen Print-Anweisungen? Wenn die Antwort auf Frage 1 "nur ein Kern" ist, habe ich einen Weg, um zu verfolgen, welches es ist?

Ich stelle mir vor, eine bestimmte CPU kann sich von einem Zeitschlitz zum anderen ändern. Du könntest so etwas wie /proc/<pid>/task/<tid>/status auf alten Linux-Kerneln anschauen . Auf meiner Maschine kann task_cpu aus /proc/<pid>/stat oder /proc/<pid>/task/<tid>/stat gelesen werden :

 >>> open("/proc/{pid}/stat".format(pid=os.getpid()), 'rb').read().split()[-14] '4' 

Für eine aktuelle tragbare Lösung, ob psutil solche Informationen aussetzt.

Sie könnten den aktuellen Prozess auf einen Satz von CPUs beschränken:

 os.sched_setaffinity(0, {0}) # current process on 0-th core 

Q: Für diese Frage vergessen wir über Threads, aber wir konzentrieren uns auf den Subprozess-Mechanismus in Python. Das Starten eines neuen Teilprozesses impliziert das Starten einer neuen Python-Interpreter-Session / Shell. Ist das richtig?

Ja. subprocess Modul schafft neue OS-Prozesse. Wenn du python ausführbar machst, dann startet es einen neuen Python Interpeter. Wenn Sie ein bash-Skript ausführen, dann wird kein neuer Python-Interpreter erstellt, dh mit der Ausführung von bash executable startet kein neuer Python-Interpreter / Session / etc.

Q: Angenommen, dass es richtig ist, wird Python schlau genug sein, um diese separate Dolmetschersitzung auf einem anderen CPU-Kern laufen zu lassen? Gibt es eine Möglichkeit, dies zu verfolgen, vielleicht auch mit einigen sporadischen Druckaussagen?

Siehe oben (dh, OS entscheidet, wo Sie Ihren Thread laufen lassen und es könnte OS API geben, die ausstellt, wo der Thread ausgeführt wird).

multiprocessing.Process(target=foo, args=(q,)).start()

multiprocessing.Process erstellt auch einen neuen OS-Prozess (der einen neuen Python-Interpreter ausführt).

In Wirklichkeit ist mein Unterprozeß eine andere Akte. So wird dieses Beispiel für mich nicht funktionieren.

Python verwendet Module, um den Code zu organisieren. Wenn Ihr Code in another_file.py dann import another_file in deinem Hauptmodul und passiere another_file.foo zu multiprocessing.Process .

Trotzdem, wie würden Sie es mit p = subprocess.Popen (..) vergleichen? Ist es wichtig, wenn ich den neuen Prozess anfange (oder soll ich 'python interpreter instance' sagen) mit subprocess.Popen (..) versus multiprocessing.Process (..)?

multiprocessing.Process() ist wahrscheinlich auf der obere subprocess.Popen() implementiert.Popen subprocess.Popen() . multiprocessing bietet API, das ähnlich wie threading API ist und es abstrahiert Details der Kommunikation zwischen Python-Prozesse (wie Python-Objekte serialisiert werden, um zwischen Prozessen gesendet werden).

Wenn es keine CPU-intensiven Aufgaben gibt, dann könntest du deine GUI- und I / O-Threads in einem einzigen Prozess ausführen. Wenn Sie eine Reihe von CPU-intensiven Aufgaben haben, dann mehrere CPUs gleichzeitig nutzen, verwenden Sie mehrere Threads mit C-Erweiterungen wie z. B. lxml , regex , numpy (oder Ihre eigene, die mit Cython erstellt wurde ), die GIL während langer Berechnungen freigeben oder auslösen kann In getrennte Prozesse (ein einfacher Weg ist, einen Prozess-Pool, wie von concurrent.futures zur Verfügung gestellt) zu verwenden.

Q: Die Gemeinschaftsdiskussion hat eine neue Frage gestellt. Es gibt anscheinend zwei Ansätze beim Laichen eines neuen Prozesses (innerhalb einer neuen Python-Interpreter-Instanz):

 # Approach 1(a) p = subprocess.Popen(['python', mySubprocessPath], shell = True) # Approach 1(b) (JF Sebastian) p = subprocess.Popen([sys.executable, mySubprocessPath]) # Approach 2 p = multiprocessing.Process(target=foo, args=(q,)) 

"Approach 1 (a)" ist auf POSIX falsch (obwohl es bei Windows funktionieren kann). Für Portabilität verwenden Sie "Approach 1 (b)", wenn Sie nicht wissen, dass Sie cmd.exe benötigen (übergeben Sie einen String in diesem Fall, um sicherzustellen, dass die korrekte Befehlszeilen-Escaping verwendet wird).

Der zweite Ansatz hat den offensichtlichen Nachteil, dass es nur eine Funktion zielt – während ich ein neues Python-Skript eröffnen muss. Wie auch immer, sind beide Ansätze ähnlich in dem, was sie erreichen?

subprocess schafft neue prozesse, beliebige prozesse zB kannst du ein bash script ausführen. multprocessing wird verwendet, um Python-Code in einem anderen Prozess auszuführen. Es ist flexibler, ein Python-Modul zu importieren und seine Funktion auszuführen, als es als Skript auszuführen. Siehe Call python-Skript mit Eingabe mit in einem Python-Skript mit Unterprozess .

Da Sie das threading Modul verwenden, das auf thread . Wie die Dokumentation vorschlägt, verwendet es die '' POSIX Thread Implementierung '' pthread Ihres Betriebssystems.

  1. Die Threads werden vom OS anstelle von Python-Interpreter verwaltet. Also hängt die Antwort von der pthread-Bibliothek in deinem System ab. CPython verwendet jedoch GIL, um zu verhindern, dass mehrere Threads Python-Bytecodes simutan ausführen. Also werden sie sequentialisiert. Aber trotzdem können sie auf verschiedene Kerne getrennt werden, was von deinen pthread libs abhängt.
  2. Verwenden Sie einfach einen Debugger und fügen Sie ihn an Ihre python.exe. Zum Beispiel der GDB-Thread-Befehl .
  3. Ähnlich wie bei Frage 1 wird der neue Prozess von Ihrem Betriebssystem verwaltet und läuft wahrscheinlich auf einem anderen Kern. Verwenden Sie Debugger oder einen Prozessmonitor, um ihn zu sehen. Weitere Informationen finden Sie auf der CreatProcess() Dokumentationsseite.

1, 2: Du hast drei echte Threads, aber in CPython sind sie von GIL begrenzt, also unter der Annahme, dass sie reine Python laufen, Code du die CPU-Auslastung sehen wirst, als ob nur ein Kern verwendet würde.

3: Wie gesagt gdlmx ist es bis zu OS, um einen Kern zu wählen, um einen Thread zu laufen, aber wenn du wirklich Kontrolle braucht, kannst du Prozess- oder Thread-Affinität mit nativen API über ctypes . Da du auf Windows bist, wäre es so:

 # This will run your subprocess on core#0 only p = subprocess.Popen(['python', mySubprocessPath], shell = True) cpu_mask = 1 ctypes.windll.kernel32.SetProcessAffinityMask(p._handle, cpu_mask) 

Ich benutze hier private Popen._handle für die Einfachheit. Der saubere Weg wäre OpenProcess(p.tid) etc.

Und ja, subprocess läuft python wie alles andere in einem anderen neuen Prozess.

  • Was bedeutet print (... sep = '', '\ t') bedeutet?
  • Warum ist meine Python3-Skript-Balk bei Piping ihre Ausgabe an Kopf oder Schwanz (Sys-Modul)?
  • Kann die Methode des Elternteils im Listenverständnis im Initialisierer des Kindes nicht aufrufen, aber explizite Schleife funktioniert
  • Parallele Berechnungsaufgabe zu Brute-Force in Python
  • Wie komme ich mehr Fäden als Prozesse, die ich nach meinem Pool in py3k Multiprocessing unter Linux gefragt habe?
  • Speichern von Wörterbüchern in Datei (numpy und Python 2/3 freundlich)
  • ValueError: benötigt mehr als 1 Wert zum Auspacken
  • Mit python ein anderes Programm ausführen?
  • PyQt4 Wurde im Thread für Benutzereingaben aus GUI
  • Eindeutig dokumentiertes Lesen von E-Mails Funktionalität mit Python Win32com Outlook
  • Unterschied zwischen super () und rufende Superklasse direkt
  • Python ist die beste Programmiersprache der Welt.