Gibt es veraltete Methoden für die Multithread- und Multiprozessor-Programmierung, die ich nicht mehr verwenden sollte?

36

In den Anfängen von FORTRAN und BASIC wurden im Wesentlichen alle Programme mit GOTO-Anweisungen geschrieben. Das Ergebnis war Spaghetti-Code und die Lösung war strukturierte Programmierung.

In ähnlicher Weise können Zeiger schwierig zu steuernde Merkmale in unseren Programmen haben. C ++ begann mit vielen Zeigern, aber die Verwendung von Referenzen wird empfohlen. Bibliotheken wie STL können einen Teil unserer Abhängigkeit verringern. Es gibt auch Redewendungen zum Erstellen intelligenter Zeiger mit besseren Eigenschaften, und in einigen Versionen von C ++ sind Verweise und verwalteter Code zulässig.

Programmierpraktiken wie Vererbung und Polymorphismus verwenden viele Zeiger hinter den Kulissen (genau wie beim strukturierten Programmieren wird Code mit Verzweigungsanweisungen generiert). Sprachen wie Java eliminieren Zeiger und verwenden Garbage Collection, um dynamisch zugewiesene Daten zu verwalten, anstatt von Programmierern abhängig zu sein, die alle ihre neuen und Löschanweisungen abgleichen.

In meiner Lektüre habe ich Beispiele für Multiprozess- und Multi-Thread-Programmierung gesehen, bei denen keine Semaphore verwendet werden. Verwenden sie dasselbe mit unterschiedlichen Namen oder haben sie neue Möglichkeiten, den Schutz von Ressourcen vor gleichzeitiger Nutzung zu strukturieren?

Ein spezielles Beispiel für ein System zur Multithread-Programmierung mit Multicore-Prozessoren ist beispielsweise OpenMP. Es stellt einen kritischen Bereich wie folgt dar, ohne die Verwendung von Semaphoren, die in der Umgebung nicht enthalten zu sein scheinen.

th_id = omp_get_thread_num();
#pragma omp critical
{
  cout << "Hello World from thread " << th_id << '\n';
}

Dieses Beispiel ist ein Auszug aus: http://en.wikipedia.org/wiki/OpenMP

Alternativ könnte ein ähnlicher Schutz von Threads voneinander durch Semaphoren mit den Funktionen wait () und signal () so aussehen:

wait(sem);
th_id = get_thread_num();
cout << "Hello World from thread " << th_id << '\n';
signal(sem);

In diesem Beispiel sind die Dinge ziemlich einfach, und nur eine einfache Überprüfung reicht aus, um zu zeigen, dass wait () - und signal () -Aufrufe übereinstimmen, und selbst bei viel Parallelität wird Threadsicherheit bereitgestellt. Andere Algorithmen sind jedoch komplizierter und verwenden mehrere Semaphore (sowohl binär als auch zählend), die über mehrere Funktionen mit komplexen Bedingungen verteilt sind, die von vielen Threads aufgerufen werden können. Die Folgen eines Deadlocks oder einer fehlgeschlagenen Thread-Sicherung können schwer zu bewältigen sein.

Beseitigen diese Systeme wie OpenMP die Probleme mit Semaphoren?
Bewegen sie das Problem woanders hin?
Wie transformiere ich mein Lieblingssemaphor mithilfe eines Algorithmus, um keine Semaphore mehr zu verwenden?

DeveloperDon
quelle
Was genau redest du? Was hast du gesehen?
Svick
4
Das bedeutet nicht, unhöflich zu sein, aber Sie hätten die ersten drei Absätze streichen können. Sie haben keinen wirklichen Einfluss auf Ihre Frage und greifen zu weit in ihre Schlussfolgerungen ein und werden nur eine Menge Argumente hervorbringen.
Dbracey
1
Whoa, große Bearbeitung. Ich habe versucht, eine Antwort zu finden. Die Frage wandert immer noch irgendwie durch GOTO, Zeiger, Vererbung und Polymorphismus, aber in meiner Antwort habe ich diese Probleme beiseite gelegt und mich auf die Frage nach "veralteten Praktiken" konzentriert.
Stuart Marks

Antworten:

15

Gibt es parallele Programmiertechniken und -praktiken, die man nicht mehr anwenden sollte? Ich würde ja sagen .

Eine frühe parallele Programmiertechnik, die heutzutage selten zu sein scheint, ist die Interrupt-gesteuerte Programmierung. So funktionierte UNIX in den 1970er Jahren. Lesen Sie den Lions- Kommentar zu UNIX oder Bachs Design des UNIX-Betriebssystems . Kurz gesagt besteht die Technik darin, Interrupts vorübergehend auszusetzen, während eine Datenstruktur manipuliert wird, und anschließend Interrupts wiederherzustellen. Die BSD spl (9) Manpagehat ein Beispiel für diesen Codierungsstil. Es ist zu beachten, dass die Interrupts hardwareorientiert sind und der Code eine implizite Beziehung zwischen der Art des Hardware-Interrupts und den dieser Hardware zugeordneten Datenstrukturen verkörpert. Beispielsweise muss Code, der Platten-E / A-Puffer manipuliert, Interrupts von der Platten-Controller-Hardware aussetzen, während er mit diesen Puffern arbeitet.

Diese Art der Programmierung wurde von Betriebssystemen auf Einprozessor-Hardware verwendet. Es war viel seltener, dass Anwendungen sich mit Interrupts befassten. Einige Betriebssysteme hatten Software-Interrupts, und ich glaube, die Leute haben versucht, darauf Threading- oder Coroutine-Systeme aufzubauen, aber das war nicht sehr verbreitet. (Sicherlich nicht in der UNIX-Welt.) Ich vermute, dass die Interrupt-Programmierung heutzutage auf kleine eingebettete Systeme oder Echtzeitsysteme beschränkt ist.

Semaphore sind ein Fortschritt gegenüber Interrupts, da sie Software-Konstrukte sind (nicht hardwarebezogen), Abstraktionen über Hardware-Einrichtungen bereitstellen und Multithreading und Multiprocessing ermöglichen. Das Hauptproblem ist, dass sie unstrukturiert sind. Der Programmierer ist dafür verantwortlich, die Beziehung zwischen jedem Semaphor und den Datenstrukturen, die er schützt, global im gesamten Programm aufrechtzuerhalten. Aus diesem Grund denke ich, dass bloße Semaphore heutzutage selten verwendet werden.

Ein weiterer kleiner Schritt nach vorne ist ein Monitor , der Kontrollmechanismen (Sperren und Bedingungen) für den gleichzeitigen Zugriff auf die zu schützenden Daten kapselt. Dies wurde in das Mesa- System (Alternate Link) und von dort nach Java übertragen. (Wenn Sie dieses Mesa-Dokument lesen, können Sie feststellen, dass Javas Monitorsperren und -bedingungen fast wörtlich von Mesa kopiert wurden.) Monitore sind hilfreich, da ein ausreichend vorsichtiger und sorgfältiger Programmierer gleichzeitige Programme sicher schreiben kann, indem er nur lokale Argumente zu Code und Daten verwendet innerhalb des Monitors.

Es gibt zusätzliche Bibliothekskonstrukte, z. B. die im java.util.concurrentPaket von Java enthaltenen, die eine Vielzahl von hochkonkurrierenden Datenstrukturen und Thread-Pooling-Konstrukten enthalten. Diese können mit zusätzlichen Techniken kombiniert werden, wie z. B. Fadenbegrenzung und effektive Unveränderlichkeit. Siehe Java Concurrency In Practice von Goetz et. al. zur weiteren Diskussion. Unglücklicherweise rollen viele Programmierer immer noch ihre eigenen Datenstrukturen mit Sperren und Bedingungen, wenn sie eigentlich nur etwas wie ConcurrentHashMap verwenden sollten, wo die Bibliotheksautoren bereits die schwere Arbeit geleistet haben.

Alle oben genannten Elemente weisen einige wichtige Merkmale auf: Sie verfügen über mehrere Steuerungsthreads, die über einen global gemeinsam genutzten, veränderlichen Status hinweg interagieren . Das Problem ist, dass die Programmierung in diesem Stil immer noch sehr fehleranfällig ist. Es ist ziemlich einfach, einen kleinen Fehler unbemerkt zu lassen, was zu Fehlverhalten führt, das schwer zu reproduzieren und zu diagnostizieren ist. Es kann sein, dass kein Programmierer "vorsichtig und gewissenhaft genug" ist, um große Systeme auf diese Weise zu entwickeln. Zumindest sehr wenige. Daher würde ich sagen, dass Multithread-Programmierung mit gemeinsamem, veränderlichem Status nach Möglichkeit vermieden werden sollte.

Leider ist nicht ganz klar, ob dies in allen Fällen vermieden werden kann. Auf diese Weise wird immer noch viel programmiert. Es wäre schön, wenn dies durch etwas anderes ersetzt würde. Die Antworten von Jarrod Roberson und davidk01 weisen auf Techniken wie unveränderliche Daten, funktionale Programmierung, STM und Nachrichtenübermittlung hin. Es gibt viel zu empfehlen und alle werden aktiv weiterentwickelt. Aber ich denke nicht, dass sie den guten, altmodischen, geteilten, wandelbaren Zustand vollständig ersetzt haben.

EDIT: hier ist meine Antwort auf die spezifischen Fragen am Ende.

Ich weiß nicht viel über OpenMP. Mein Eindruck ist, dass es für hochparallele Probleme wie numerische Simulationen sehr effektiv sein kann. Aber es scheint nicht allgemein gültig zu sein. Die Semaphorkonstrukte scheinen ziemlich einfach zu sein und erfordern, dass der Programmierer die Beziehung zwischen Semaphoren und gemeinsam genutzten Datenstrukturen mit all den oben beschriebenen Problemen beibehält.

Wenn Sie einen parallelen Algorithmus haben, der Semaphoren verwendet, sind mir keine allgemeinen Techniken bekannt, um ihn zu transformieren. Möglicherweise können Sie es in Objekte umgestalten und anschließend einige Abstraktionen erstellen. Wenn Sie jedoch so etwas wie die Nachrichtenübermittlung verwenden möchten, müssen Sie meines Erachtens das gesamte Problem wirklich neu erfassen.

Stuart Marks
quelle
Danke, das sind großartige Informationen. Ich werde die Referenzen durchsehen und mich eingehender mit den von Ihnen erwähnten Konzepten befassen, die für mich neu sind.
DeveloperDon
+1 für java.util.concurrent und stimmte dem Kommentar zu - es ist seit 1.5 im JDK und ich sehe es selten, wenn ich es jemals benutzt habe.
MebAlone
1
Ich wünschte, Sie hätten betont, wie wichtig es ist, keine eigenen Strukturen zu rollen, wenn es bereits solche gibt. So viele, so viele Bugs ...
Corsika
Ich denke nicht, dass es richtig ist zu sagen: "Semaphoren sind ein Fortschritt gegenüber Interrupts, weil sie Softwarekonstrukte sind (die nicht mit Hardware zusammenhängen). " Semaphoren hängen von der CPU ab, um den Compare-and-Swap- Befehl oder dessen Multi-Core-Varianten zu implementieren .
Josh Pearce
@JoshPearce Natürlich werden Semaphore mithilfe von Hardwarekonstrukten implementiert , aber sie sind eine Abstraktion , die unabhängig von bestimmten Hardwarekonstrukten wie CAS, Test & Set, cmpxchng usw. ist.
Stuart Marks,
28

Antwort auf die Frage

Der allgemeine Konsens ist, dass der geteilte veränderbare Zustand Bad ™ und der unveränderliche Zustand Good ™ ist, was sich auch in funktionalen Sprachen und imperativen Sprachen immer wieder als genau und wahr herausstellt.

Das Problem ist, dass die gängigen imperativen Sprachen für diese Arbeitsweise einfach nicht ausgelegt sind. Die Dinge werden sich für diese Sprachen über Nacht nicht ändern. Hier ist der Vergleich mit GOTOfehlerhaft. Unveränderliche Zustands- und Nachrichtenübermittlung ist eine großartige Lösung, aber auch kein Allheilmittel.

Fehlerhafte Prämisse

Diese Frage basiert auf Vergleichen mit einer fehlerhaften Prämisse; Das GOTOwar das eigentliche Problem und wurde von der Intergalatic Universal Board of Language Designers und Software Engineering Unions © allgemein abgelehnt. Ohne einen GOTOMechanismus würde ASM überhaupt nicht funktionieren. Dasselbe gilt für die Annahme, dass unformatierte Zeiger das Problem von C oder C ++ sind, und für einige, dass intelligente Zeiger ein Allheilmittel sind.

GOTOwar nicht das Problem, Programmierer waren das Problem. Gleiches gilt für den gemeinsam genutzten veränderlichen Zustand . Es ist an und für sich nicht das Problem , sondern das Problem sind die Programmierer, die es verwenden. Wenn es eine Möglichkeit gäbe, Code zu generieren, der den gemeinsam genutzten veränderlichen Status auf eine Weise verwendet, die niemals Rennbedingungen oder Fehler aufweist, dann wäre dies kein Problem. GOTOEs ist auch kein Problem, wenn Sie niemals Spaghetti-Code mit oder gleichwertige Konstrukte schreiben .

Bildung ist das Allheilmittel

Idiotische Programmierer sind das deprecated, was waren , jede populäre Sprache hat immer noch das GOTOKonstrukt entweder direkt oder indirekt und es ist ein, best practicewenn es richtig in jeder Sprache verwendet wird, die diese Art von Konstrukten hat.

Beispiel: Java hat Etiketten und try/catch/finallybeide direkt als arbeiten GOTOAussagen.

Die meisten Java-Programmierer, mit denen ich spreche, wissen nicht einmal, was es immutablebedeutet , wenn sie sich the String class is immutablemit einem zombieähnlichen Blick in den Augen wiederholen . Sie wissen definitiv nicht, wie man das finalSchlüsselwort richtig verwendet , um eine immutableKlasse zu erstellen . Ich bin mir also ziemlich sicher, dass sie keine Ahnung haben, warum die Weitergabe von Nachrichten mit unveränderlichen Nachrichten so großartig ist und warum der gemeinsame veränderbare Status nicht so großartig ist.

Gemeinschaft
quelle
3
+1 Eine großartige Antwort, die klar geschrieben ist und das zugrunde liegende Muster des veränderlichen Zustands aufzeigt. IUBLDSEU sollte ein Mem werden :)
Dibbeke
2
GOTO ist ein Codewort für "Bitte, nicht wirklich, bitte fang hier einen Flammenkrieg an, ich Doppelhund wage dich". Diese Frage löscht die Flammen, gibt aber keine wirklich gute Antwort. Lobende Erwähnungen der funktionalen Programmierung und der Unveränderlichkeit sind großartig, aber diese Aussagen enthalten kein Fleisch.
Evan Plaice
1
Dies scheint eine widersprüchliche Antwort zu sein. Zuerst sagst du "A ist schlecht, B ist gut", dann sagst du "Idioten waren veraltet". Gilt das nicht auch für den ersten Absatz? Kann ich nicht einfach den letzten Teil Ihrer Antwort nehmen und sagen, dass der gemeinsame veränderbare Zustand eine bewährte Methode ist, wenn er in jeder Sprache richtig verwendet wird? Auch "Beweis" ist ein sehr starkes Wort. Sie sollten es nicht verwenden, es sei denn, Sie haben wirklich starke Beweise.
Luiscubal,
2
Es war nicht meine Absicht, einen Flammenkrieg zu beginnen. Bis Jarrod auf meinen Kommentar reagierte, hatte er gedacht, dass GOTO unumstritten sei und in Analogie gut funktionieren würde. Als ich die Frage schrieb, fiel es mir nicht ein, aber Dijkstra war sowohl bei GOTOs als auch bei Semaphoren auf dem Nullpunkt. Edsger Dijkstra wirkt für mich wie ein Riese und wurde mit der Erfindung von Semaphoren (1965) und frühen (1968) wissenschaftlichen Arbeiten über GOTOs in Verbindung gebracht. Dijkstras Advocacy-Methode war oft krustig und konfrontativ. Kontroverse / Konfrontation funktionierte für ihn, aber ich möchte nur Ideen über mögliche Alternativen zu Semaphoren.
DeveloperDon
1
Viele Programme sollen Dinge modellieren, die in der realen Welt veränderlich sind. Wenn Objekt # 451 um 5:37 Uhr den Zustand von etwas in der realen Welt in diesem Moment enthält (5:37 Uhr) und sich der Zustand der realen Welt anschließend ändert, ist es möglich, dass entweder das Objekt die Identität darstellt der Zustand der realen Sache, um unveränderlich zu sein (dh die Sache wird immer durch Objekt # 451 dargestellt), oder für Objekt # 451, um unveränderlich zu sein, aber nicht beide. In vielen Fällen ist es hilfreicher, wenn die Identität unveränderlich ist, als wenn das Objekt Nr. 451 unveränderlich ist.
Superkatze
27

Die neueste Wut in akademischen Kreisen scheint Software Transactional Memory (STM) zu sein, und es verspricht, den Programmierern mit Hilfe einer ausreichend intelligenten Compilertechnologie alle haarigen Details der Multithread-Programmierung aus den Händen zu nehmen. Hinter den Kulissen gibt es immer noch Sperren und Semaphoren, aber Sie als Programmierer müssen sich darüber keine Gedanken machen. Die Vorteile dieses Ansatzes sind immer noch nicht klar und es gibt keine offensichtlichen Konkurrenten.

Erlang verwendet Nachrichtenübermittlung und Agenten für die gleichzeitige Verwendung. Dies ist ein einfacheres Modell als STM. Bei der Weitergabe von Nachrichten müssen Sie sich keine Gedanken über Sperren und Semaphoren machen, da jeder Agent in seinem eigenen Mini-Universum arbeitet und somit keine datenbezogenen Wettkampfbedingungen vorliegen. Sie haben immer noch einige seltsame Fälle, aber sie sind bei weitem nicht so kompliziert wie Livelocks und Deadlocks. JVM-Sprachen können Akka verwenden und alle Vorteile von Message Passing und Schauspielern nutzen. Im Gegensatz zu Erlang bietet JVM jedoch keine eingebaute Unterstützung für Schauspieler, sodass Akka am Ende des Tages weiterhin Threads und Sperren verwendet, Sie jedoch Programmierer müssen sich nicht darum kümmern.

Das andere Modell, von dem ich weiß, dass es keine Sperren und Threads verwendet, ist die Verwendung von Futures, die eigentlich nur eine andere Form der asynchronen Programmierung ist.

Ich bin nicht sicher, wie viel von dieser Technologie in C ++ verfügbar ist, aber wenn Sie etwas sehen, das nicht explizit Threads und Sperren verwendet, wird es eine der oben genannten Techniken für die Verwaltung der Parallelität sein.

davidk01
quelle
+1 für den neuen Begriff "haarige Details". LOL Mann. Ich kann einfach nicht aufhören über diese neue Amtszeit zu lachen. Ich denke, ich werde von nun an "haarigen Code" verwenden.
Saeed Neamati
1
@ Saeed: Ich habe diesen Ausdruck schon einmal gehört, es ist nicht ungewöhnlich. Ich bin damit einverstanden, dass es lustig ist :-)
Cameron
1
Gute Antwort. Die .NET-CLI hat angeblich auch Unterstützung für die Signalisierung (im Gegensatz zur Sperrung), aber ich habe noch kein Beispiel gefunden, in dem die Sperrung vollständig ersetzt wurde. Ich bin mir nicht sicher, ob Async zählt. Wenn es sich um Plattformen wie Javascript / NodeJs handelt, sind sie in Wirklichkeit Single-Thread-fähig und nur bei hoher E / A-Belastung besser, da sie weniger anfällig für maximale Ressourcenbeschränkungen sind (z. B. in einer Menge wegwerfbarer Kontexte). Bei CPU-intensiven Lasten hat die Verwendung der asynchronen Programmierung nur geringe oder gar keine Vorteile.
Evan Plaice
1
Interessante Antwort, ich war noch nie auf Futures gestoßen. Beachten Sie auch, dass in Nachrichtenübermittlungssystemen wie Erlang weiterhin Deadlocks und Livelocks auftreten können . Mit CSP können Sie formell über Deadlock und Livelock nachdenken, aber es verhindert es nicht von selbst.
Mark Booth
1
Ich würde Lock Free und Wait Free Datenstrukturen zu dieser Liste hinzufügen.
Stonemetal
3

Ich denke, es geht hauptsächlich um Abstraktionsebenen. Häufig ist es beim Programmieren hilfreich, einige Details auf eine sicherere oder besser lesbare Weise oder auf eine ähnliche Weise zu abstrahieren.

Dies gilt für Kontrollstrukturen: ifs, fors und sogar try- catchBlöcke sind nur Abstraktionen über gotos. Diese Abstraktionen sind fast immer nützlich, weil sie den Code besser lesbar machen. Es gibt jedoch Fälle, in denen Sie noch etwas verwenden müssen goto(z. B. wenn Sie Assemblierungen von Hand schreiben).

Dies gilt auch für die Speicherverwaltung: C ++ Smart Pointer und GC sind Abstraktionen über unformatierte Zeiger und manuelle Speicherfreigabe / -zuweisung. Und manchmal sind diese Abstraktionen nicht geeignet, z. B. wenn Sie wirklich maximale Leistung benötigen.

Gleiches gilt für Multi-Threading: Dinge wie Futures und Actors sind nur Abstraktionen über Threads, Semaphoren, Mutexe und CAS-Anweisungen. Solche Abstraktionen können Ihnen dabei helfen, Ihren Code besser lesbar zu machen und Fehler zu vermeiden. Aber manchmal sind sie einfach nicht angemessen.

Sie sollten wissen, welche Tools Ihnen zur Verfügung stehen und welche Vor- und Nachteile sie haben. Dann können Sie die richtige Abstraktion für Ihre Aufgabe auswählen (falls vorhanden). Höhere Abstraktionsebenen setzen niedrigere Ebenen nicht außer Kraft. In einigen Fällen ist die Abstraktion nicht angemessen und die beste Wahl ist die Verwendung des „alten Wegs“.

svick
quelle
Danke, Sie verstehen die Analogie, und ich habe keine vorgefasste Idee oder Axt, um zu erörtern, ob die Antwort-WRT-Semaphoren sind, dass sie veraltet sind oder nicht. Die größere Frage für mich ist, ob es bessere Möglichkeiten gibt und ob in Systemen, in denen Semaphoren anscheinend nichts Wichtiges fehlen, nicht alle Multithread-Algorithmen ausgeführt werden können.
DeveloperDon
2

Ja, aber einige davon werden Ihnen wahrscheinlich nicht begegnen.

In früheren Zeiten war es üblich, Blockierungsmethoden (Barrier-Synchronisation) zu verwenden, da es schwierig war, gute Mutexe richtig zu schreiben. Sie können immer noch Spuren davon in den letzten Jahren sehen. Die Verwendung moderner Parallelitätsbibliotheken bietet Ihnen eine viel umfangreichere und gründlich getestete Reihe von Werkzeugen für die Parallelisierung und die Koordination zwischen Prozessen.

Ebenso bestand eine ältere Praxis darin, quälenden Code so zu schreiben, dass Sie herausfinden konnten, wie man ihn manuell parallelisiert. Diese Form der (potenziell schädlichen, wenn Sie es falsch verstehen) Optimierung ist auch mit dem Aufkommen von Compilern, die dies für Sie tun, größtenteils aus dem Fenster verschwunden, die Schleifen bei Bedarf abwickeln, Zweigen vorausschauend folgen usw. Dies ist jedoch keine neue Technologie , mindestens 15 Jahre auf dem Markt. Das Ausnutzen von Dingen wie Thread-Pools umgeht auch einen wirklich kniffligen Code von gestern.

Vielleicht schreibt die veraltete Praxis den Parallelitätscode selbst, anstatt moderne, gut getestete Bibliotheken zu verwenden.

Alex Feinman
quelle
Vielen Dank. Es scheint, als gäbe es ein großes Potenzial für die gleichzeitige Verwendung von Programmen, aber es könnte sich um eine Büchse der Pandora handeln, wenn sie nicht diszipliniert eingesetzt wird.
DeveloperDon
2

Apples Grand Central Dispatch ist eine elegante Abstraktion, die mein Denken über Parallelität verändert hat. Die Konzentration auf Warteschlangen macht die Implementierung asynchroner Logik meiner bescheidenen Erfahrung nach um eine Größenordnung einfacher.

Wenn ich in Umgebungen programmiere, in denen es verfügbar ist, hat es die meisten meiner Verwendungen von Threads, Sperren und Kommunikation zwischen Threads ersetzt.

orip
quelle
1

Eine der wichtigsten Änderungen bei der parallelen Programmierung besteht darin, dass CPUs erheblich schneller sind als zuvor. Um diese Leistung zu erzielen, ist jedoch ein gut gefüllter Cache erforderlich. Wenn Sie versuchen, mehrere Threads gleichzeitig fortlaufend auszutauschen, wird der Cache für jeden Thread fast immer ungültig (dh für jeden Thread sind unterschiedliche Daten erforderlich), und die Leistung wird am Ende weitaus höher als bei Ihnen gewohnt mit langsameren CPUs.

Dies ist einer der Gründe, warum asynchrone oder aufgabenbasierte Frameworks (z. B. Grand Central Dispatch oder Intels TBB) beliebter sind. Sie führen jeweils eine Aufgabe mit Code 1 aus und beenden diese, bevor Sie zur nächsten übergehen. Sie müssen jedoch jede Aufgabe codieren Jede Aufgabe nimmt nur wenig Zeit in Anspruch, es sei denn, Sie möchten den Entwurf schrauben (dh Ihre parallelen Aufgaben stehen wirklich in der Warteschlange). CPU-intensive Aufgaben werden an einen alternativen CPU-Kern übergeben, anstatt alle Aufgaben in einem einzelnen Thread zu verarbeiten. Es ist auch einfacher zu verwalten, wenn keine wirklich Multithread-Verarbeitung stattfindet.

gbjbaanb
quelle
Cool, danke für Hinweise auf Apple und Intel Tech. Zeigt Ihre Antwort die Herausforderungen beim Verwalten des Threads in Bezug auf die Kernaffinität auf? Einige Probleme mit der Cache-Leistung werden behoben, weil Multicore-Prozessoren L1-Caches pro Kern wiederholen können. Beispiel: software.intel.com/de-de/articles/... Hochgeschwindigkeits-Cache für vier Kerne mit mehr Cache-Treffern kann mehr als viermal so schnell sein wie ein Kern mit mehr Cache-Fehlern bei denselben Daten. Matrixmultiplikation kann. Die zufällige Planung von 32 Threads auf 4 Kernen ist nicht möglich. Lassen Sie uns die Affinität verwenden und 32 Kerne erhalten.
DeveloperDon
Nicht wirklich, obwohl es das gleiche Problem ist - Kernaffinität bezieht sich nur auf das Problem, bei dem eine Aufgabe von Kern zu Kern übertragen wird. Es ist das gleiche Problem, wenn eine Aufgabe unterbrochen und durch eine neue ersetzt wird und die ursprüngliche Aufgabe auf demselben Kern fortgesetzt wird. Intel sagt dort: Cache-Treffer = schnell, Cache-Fehlschläge = langsam, unabhängig von der Anzahl der Kerne. Ich denke, sie versuchen, Sie zu überreden, ihre Chips anstatt AMDs zu kaufen :)
gbjbaanb