Ich entschuldige mich im Voraus für das Blog-Post-Format meiner Antwort. Ich konnte nicht anders, als mir einen kleinen Überblick über die Parallel-Computing-Welt zu verschaffen.
Sie können parallele Programmiermodelle in ungefähr zwei Kategorien einteilen: Kontrollfluss- und Datenflussmodelle.
Die Kontrollflussmodelle versuchen, Parallelität im Kontext eines expliziten Kontrollprogramms zu ermöglichen, im Grunde genommen jeden heute programmierbaren Computer. Das grundlegende Problem besteht darin, dass eine solche "Von Neumann-Architektur" nicht für die parallele Ausführung, sondern für effiziente sequentielle Berechnungen konzipiert wurde. Parallelität in einem solchen Kontext wird durch Duplizieren von Teilen der Grundmodule (Speicher, Steuerung, Arithmetik) erhalten.
Wenn Sie nur Arithmetik duplizieren, erhalten Sie SIMD-Anweisungen. Alle ALUs verwenden denselben Programmzähler (PC) und führen daher immer den gleichen Vorgang parallel aus, wenn auch mit unterschiedlichen Daten.
Wenn Sie ALU und PC duplizieren, aber den Anweisungssequenzer in der Steuereinheit belassen, erhalten Sie eine Out-of-Order-Ausführung (OoO), die eine gewisse Pipeline-Parallelität ergibt. In dieser Kategorie finden Sie auch das VLWI (Very Long Instruction Word) und Techniken zur Branchenvorhersage. Sie sehen diese Kategorie jedoch selten auf Software-Ebene.
Ein bisschen weiter zu gehen bedeutet, den gesamten "Kern" zu duplizieren, aber den Speicher gemeinsam zu nutzen. Dies sind die aktuellen Multicore-Prozessoren, mit denen Sie Task- (oder Thread-) Parallelität erzielen. Wenn Sie in diesem Zusammenhang den Speicher gemeinsam nutzen, treten sehr , sehr schwierige und subtile Probleme bei der gemeinsamen Nutzung auf . Parallele Berechnungen auf aktuellen Multicores drehen sich daher vollständig um Synchronisations- / Parallelitätsprobleme, das sorgfältige Gleichgewicht zwischen Leistung (keine Synchronisation) und gewünschter Semantik (vollständig synchronisierte, sequentielle Ausführungssemantik). Beispiele hierfür sind der PRAM oder heutzutage populärer die Cilk ofshoots wie Fork / Join ( IntelTBB , Java.Utils.Concurrency)). CSP- und Actor-Modelle sind Parallelitätsmodelle, aber wie oben erwähnt verschwimmen Parallelität und Parallelität in einer Shared-Memory-Umgebung. nb Parallelität dient der Leistung und der Aufrechterhaltung der korrekten Semantik.
Durch das Duplizieren des Speichers erhalten Sie entweder vernetzte Computer, die mit MPI und seiner Art programmiert wurden, oder nur merkwürdige Nicht-Von-Neumann-Architekturen wie die Network-on-a-Chip-Prozessoren (Cloud-Prozessor, Transputer, Tilera). Speichermodelle wie UMA oder NUMA versuchen, die Illusion eines gemeinsam genutzten Speichers aufrechtzuerhalten und können entweder auf Software- oder Hardwareebene existieren. MPI behält die Parallelität auf Programmebene bei und kommuniziert nur über Message-Passing. Die Nachrichtenübermittlung wird auch auf Hardwareebene für die Kommunikation und den gemeinsamen Zugriff (Transputer) verwendet.
Die zweite Kategorie sind Datenflussmodelle . Diese wurden zu Beginn des Computerzeitalters entwickelt, um parallele Berechnungen aufzuschreiben und auszuführen, wobei das Von Neumann-Design vermieden wurde. Diese sind in den 80er Jahren aus der Mode gekommen (für Parallel Computing), nachdem die sequentielle Leistung exponentiell angestiegen ist. Viele parallele Programmiersysteme wie Google MapReduce, Microsoft Dryad oder Intel Concurrent Collections sind jedoch tatsächlich Datenfluss-Rechenmodelle. Irgendwann stellen sie Berechnungen als Grafik dar und verwenden diese, um die Ausführung zu steuern.
Durch die Angabe von Teilen des Modells erhalten Sie unterschiedliche Kategorien und Semantiken für das Datenflussmodell. Wie beschränken Sie die Form des Graphen auf: DAG (CnC, Dryad), Baum (Mapreduce), Digraph? Gibt es eine strikte Synchronisationssemantik ( Lustre, reaktive Programmierung]? Erlauben Sie Rekursion nicht, einen statischen Zeitplan (StreaMIT) zu haben, oder bieten Sie mehr Ausdruckskraft durch einen dynamischen Zeitplan (Intel CnC)? Gibt es eine Begrenzung für die Anzahl der eingehenden oder ausgehenden Flanken? Ermöglichen die Auslösungssemantiken das Auslösen des Knotens, wenn eine Teilmenge der eingehenden Daten verfügbar ist? Sind Kanten-Datenströme (Stream-Verarbeitung) oder einzelne Datentoken (statische / dynamische Einzelzuweisung). Für verwandte Arbeiten können Sie sich zunächst die Datenflussforschung von Personen wie Arvind, K. Kavi, j. Sharp, W. Ackerman, R. Jagannathan usw.
Edit: Der Vollständigkeit halber. Ich sollte darauf hinweisen, dass es auch parallele reduktionsgetriebene und mustergetriebene Modelle gibt. Für die Reduktionsstrategien haben Sie allgemein die Graph-Reduktion und die String-Reduktion. Haskell verwendet grundsätzlich die Graph-Reduktion, eine sehr effiziente Strategie für ein sequentielles Shared-Memory-System. Duplikate zur Zeichenfolgenreduzierung funktionieren, verfügen jedoch über eine Private-Memory-Eigenschaft, die eine implizite Parallelisierung erleichtert. Die mustergetriebenen Modelle sind die parallelen Logiksprachen, beispielsweise der gleichzeitige Prolog. Das Actor-Modell ist ebenfalls ein mustergetriebenes Modell, jedoch mit Merkmalen des privaten Speichers.
PS. Ich verwende den Begriff "Modell" allgemein und spreche sowohl für formale als auch für Programmierzwecke von abstrakten Maschinen.