Wie programmiere ich die Thread-Zuweisung auf Multicore-Prozessoren?

13

Ich möchte mit Threads auf einem Multi-Core-Prozessor experimentieren, z. B. um ein Programm zu erstellen, das zwei verschiedene Threads verwendet, die von zwei verschiedenen Prozessorkernen ausgeführt werden.

Mir ist jedoch nicht klar, auf welcher Ebene die Threads den verschiedenen Kernen zugeordnet werden. Ich kann mir folgende Szenarien vorstellen (abhängig vom Betriebssystem und der Implementierung der Programmiersprache):

  1. Die Thread-Zuweisung wird vom Betriebssystem verwaltet. Threads werden mithilfe von Betriebssystemaufrufen erstellt. Wenn der Prozess auf einem Mehrkernprozessor ausgeführt wird, versucht das Betriebssystem automatisch, verschiedene Threads auf verschiedenen Kernen zuzuweisen / zu planen.
  2. Die Thread-Zuweisung wird von der Implementierung der Programmiersprache verwaltet. Das Zuweisen von Threads zu einem anderen Kern erfordert spezielle Systemaufrufe, aber die Standard-Thread-Bibliotheken der Programmiersprache behandeln dies automatisch, wenn ich die Standard-Thread-Implementierung für diese Sprache verwende.
  3. Die Threadzuordnung muss explizit programmiert werden. In meinem Programm muss ich expliziten Code schreiben, um festzustellen, wie viele Kerne verfügbar sind, und um verschiedenen Kernen unterschiedliche Threads zuzuweisen, indem ich z. B. Bibliotheksfunktionen verwende.

Um die Frage zu präzisieren, stelle ich mir vor, ich hätte meine Multithread-Anwendung in Java oder C ++ unter Windows oder Linux geschrieben. Wird meine Anwendung auf magische Weise mehrere Kerne sehen und verwenden, wenn sie auf einem Multi-Core-Prozessor ausgeführt wird (da alles entweder vom Betriebssystem oder von der Standard-Thread-Bibliothek verwaltet wird), oder muss ich meinen Code ändern, um die mehreren Kerne zu erkennen ?

Giorgio
quelle

Antworten:

11

Wird meine Anwendung auf magische Weise mehrere Kerne sehen und verwenden, wenn sie auf einem Multi-Core-Prozessor ausgeführt wird (da alles entweder vom Betriebssystem oder von der Standard-Thread-Bibliothek verwaltet wird), oder muss ich meinen Code ändern, um die mehreren Kerne zu erkennen ?

Einfache Antwort: Ja, es wird in der Regel durch das Betriebssystem oder Threading - Bibliothek verwaltet werden.

Das Threading-Subsystem im Betriebssystem weist Prozessoren Threads nach Priorität zu (Ihre Option 1). Mit anderen Worten sucht der Scheduler nach dem Thread mit der nächsthöheren Priorität und weist den Thread der CPU zu, wenn die Ausführung eines Threads für seine Zeitzuweisung oder seine Blöcke beendet ist. Die Details variieren von Betriebssystem zu Betriebssystem.

Es gibt jedoch die Optionen 2 (von der Programmiersprache verwaltet) und 3 (explizit). Beispielsweise bieten die Tasks-Bibliothek und async / await in neueren Versionen von .Net dem Entwickler eine viel einfachere Möglichkeit, parallelisierbaren Code zu schreiben (der gleichzeitig mit sich selbst ausgeführt werden kann). Funktionale Programmiersprachen sind von Haus aus parallelisierbar, und einige Laufzeiten führen, wenn möglich, verschiedene Teile des Programms parallel aus.

Wie bei Option 3 (explizit) können Sie in Windows die Thread-Affinität festlegen (indem Sie angeben, auf welchen Prozessoren ein Thread ausgeführt werden kann). Dies ist jedoch in der Regel nur in den schnellsten, reaktionszeitkritischen Systemen erforderlich. Die effektive Zuordnung von Thread zu Prozessor hängt stark von der Hardware ab und reagiert sehr empfindlich auf andere Anwendungen, die gleichzeitig ausgeführt werden.

Wenn Sie experimentieren möchten, erstellen Sie eine lang andauernde, CPU-intensive Aufgabe wie das Generieren einer Liste von Primzahlen oder das Erstellen eines Mandelbrot-Sets. Erstellen Sie nun zwei Threads in Ihrer Lieblingsbibliothek und führen Sie beide Threads auf einem Multiprozessor-Computer aus (mit anderen Worten, fast alles, was in den letzten Jahren veröffentlicht wurde). Beide Aufgaben sollten ungefähr zur gleichen Zeit ausgeführt werden, da sie parallel ausgeführt werden.

akton
quelle
Danke für die Erklärung (+1). Mein Testprogramm ist eine Mergesort-Implementierung. In der Split-Phase möchte ich verschiedene Threads erstellen, solange Kerne verfügbar sind. Beispiel: Bei zwei Kernen wird jede Hälfte eines Arrays nach einem anderen Thread / Kern sortiert. Während des Zusammenführens würden die überflüssigen Threads dann verbunden / beendet.
Giorgio
Eine solche Parallelisierung der Sortierung ist schwierig, wenn die Daten zufällig verteilt werden. Ja, Sie können es aufteilen und dann jede Portion in einem anderen Thread sortieren, aber schließlich müssen Sie sowieso alle Portionen zusammenführen. Wenn die Threads Datenstrukturen gemeinsam nutzen, kann es auch zu Konflikten oder Sperren kommen. Ich sage nicht, dass das Sortieren nicht vom Threading profitieren kann, aber es wird keine lineare Leistungsverbesserung sein.
Akton
Die beiden Hälften eines Arrays können unabhängig voneinander sortiert werden, da keine Daten gemeinsam genutzt werden. Nur die erste Aufteilung und die letzte Zusammenführung müssen von einem Thread durchgeführt werden, der das gesamte Array oder die Liste mit den Daten bearbeitet. Dies bedeutet, dass ein vollständiger Scan der Daten nicht parallel ausgeführt werden kann. alle verbleibenden scans können.
Giorgio
Natürlich betrachte ich Ihre Beispiele auch als gute Kandidaten. Ich bin im Moment nur besser mit Merge Sort vertraut (und ich habe eine nicht parallele Version davon implementiert), wodurch Merge Sort für mich (vielleicht) als erster Versuch besser geeignet wäre.
Giorgio
2
Ich möchte zu dieser Antwort hinzufügen, dass gute Betriebssysteme klug genug sind, um die Kosten für die Vergabe einer Zeitscheibe auf einer anderen CPU oder einem anderen Kern mit den Kosten für den kurzfristigen Hunger auszugleichen. Auf Architekturen, auf die es ankommt, ähnelt das Ergebnis in der Regel einer automagischen Affinität. Das Betriebssystem ist darauf ausgelegt, dass alle Jobs so schnell wie möglich ausgeführt werden, und Sie können sich in den Fuß schießen, indem Sie Fäden an Kerne binden und die Fähigkeit behindern, diese Entscheidungen zu treffen.
Blrfl
-1

Ich hatte einmal eine riesige SGI-IRIX-Umgebung. Zum Teufel habe ich ein kleines Java-Programm mit mehreren Threads geschrieben (das nur CPU-Zyklen verbraucht) und darin 12 Threads erstellt. Der Auftrag umfasste 12 CPUs in NUMA-Architektur. Vielleicht werde ich das Programm nachschlagen und es auf dem Dell R910s ausführen und prüfen ..

P. Prabhakar
quelle
3
Diese Antwort fügt der vorhandenen Antwort nicht wirklich viel hinzu. Vielleicht, wenn Sie herausgefunden haben, warum die JVM auf dem SGI-System dem Kern Threads zugeteilt hat ...
Jay Elston