Kollektive Operationen sind Grundbausteine für Interaktionsmuster von Prozessen der elektronischen Datenverarbeitung, die häufig Anwendung in SPMD-Algorithmen und paralleler Programmierung finden. Dadurch entsteht die Notwendigkeit, diese Operationen effizient zu realisieren.

Das Message Passing Interface[1] (MPI) stellt eine Realisierung der kollektiven Operationen bereit.

Definitionen

Bearbeiten

In der asymptotischen Laufzeitanalyse sei die Latenz  , die Kommunikationszeit pro Wort  , die Anzahl der Prozessoreinheiten   und die Größe der Eingabe pro Knoten  . Für Operationen, die mit Nachrichten auf verschiedenen Prozessoreinheiten starten, nehmen wir an, dass alle lokalen Nachrichten die gleiche Größe haben. Um einzelne Prozessoreinheiten zu bezeichnen, verwenden wir  .

Aus den angegebenen Laufzeiten lässt sich eine obere Schranke für den Fall bestimmen, dass die initialen Nachrichten unterschiedliche Größen haben. Habe Prozessoreinheit   eine Nachricht der Größe  . Dann setze man  .

Es wird das Modell eines verteilten Speichers angenommen. Die vorgestellten Konzepte sind im Modell eines geteilten Speichers ähnlich. Bei geteiltem Speicher besteht jedoch die Möglichkeit, dass die Hardware Operationen wie Broadcast unmittelbar unterstützt[2]. Diese Unterstützung öffnet in der Entwicklung von Algorithmen zusätzliche Möglichkeiten.

Broadcast

Bearbeiten
 
Informationsfluss von Broadcast ausgeführt auf drei Einheiten.

Das Broadcast-Muster wird genutzt, um Daten einer Prozessoreinheit an alle anderen Prozessoreinheiten zu verteilen.[3] Ein Anwendungsfall des Broadcast ist, in SPMD parallelen Programmen Eingaben und globale Variablen zu verteilen. Der Broadcast kann als inverse Reduktion aufgefasst werden. Zu Beginn enthält die Wurzel   für ein festes   die Nachricht  . Hier nehmen wir   an, um die Erklärung simpler zu gestalten. Während des Broadcast wird   an die restlichen Prozessoreinheiten gesendet, sodass   schlussendlich auf allen Prozessoreinheiten verfügbar ist.

Da die triviale Implementierung, die in   Iterationen jeweils   direkt von   an   übermittelt, nicht ausreichend performant ist, wird ein Ansatz, der das Prinzip 'Teile-und-herrsche' nutzt, verwendet. Sofern   eine Zweierpotenz ist, kann ein Binomialbaum als unterliegende Struktur verwendet werden. Angenommen Prozessoreinheit   ist verantwortlich, die Nachricht an Prozessoreinheiten   weiterzuleiten. Dann sendet   die Nachricht   an   mit  . Die Verantwortung für die Übermittlung von   an Prozessoreinheiten mit Indizes   wird an   übertragen,   ist im Folgenden nur noch für die Übermittlung von   an die Prozessoreinheiten mit Indizes   zuständig. Die Performance des Binomialbaum-Broadcast ist für lange Nachrichten nicht gut, da eine Prozessoreinheit, die   empfängt, erst dann die Nachricht weiterleiten kann, wenn   vollständig empfangen wurde. Als Ausgleich wird Pipelining verwendet. Dabei wird   in ein Array aus   Paketen der Größe   zerlegt. Die Pakete werden dann nacheinander per Broadcast verteilt, was bessere Auslastung des Kommunikationsnetzes erlaubt.

Broadcast mit Pipelining auf einem balancierten Binärbaum ist in Laufzeit   möglich.

Reduktion

Bearbeiten
 
Informationsfluss von Reduktion ausgeführt auf drei Einheiten. f sei ein assoziativer Operator und α sei das Resultat der Reduktion.

Das Muster Reduktion wird genutzt, um Daten oder partielle Ergebnisse verschiedener Prozessoreinheiten zu sammeln und in ein globales Resultat zu vereinigen.[4] Reduktion kann als inverse Operation zum Broadcast (#Broadcast) aufgefasst werden. Sei   ein assoziativer Operator,   die Prozessoreinheit, auf der das Ergebnis gespeichert werden soll. Dann berechnet die Reduktion das Ergebnis   und speichert es auf Prozessoreinheit  . Manche Algorithmen fordern, dass   zusätzlich kommutativ ist. Häufige Operatoren sind  .

Da Reduktion als inverser Broadcast aufgefasst werden kann, gelten die gleichen Randbedingungen für eine Implementierung. Um Pipelining zu ermöglichen, ist es wichtig, dass die Nachricht als Vektor kleinerer Objekte repräsentiert werden kann, sodass eine komponentenweise Reduktion möglich ist.

Reduktion mit Pipelining auf einem balancierten Binärbaum ist in Zeit   möglich.

All-reduce

Bearbeiten
 
Informationsfluss von All-Reduce ausgeführt auf drei Einheiten. f sei ein assoziativer Operator und α sei das Resultat der Reduktion.

Das Muster All-reduce wird genutzt, wenn das Ergebnis einer Reduktion (#Reduktion) allen Prozessoreinheiten zur Verfügung gestellt werden soll.[5] Zu Beginn liegt auf Prozessoreinheit   die Nachricht  . Das Ergebnis   liegt nach dem All-reduce auf allen   vor. Konzeptionell entspricht All-reduce einer Reduktion mit anschließendem Broadcast (#Broadcast). Auch bei All-reduce muss   assoziativ sein.

Für lange Nachrichten spielen die gleichen Randbedingungen eine Rolle. Für kurze Nachrichten kann die Latenz durch Nutzung einer Hyperwürfel-Topologie verbessert werden, sofern   eine Zweierpotenz ist.

Wir sehen, dass All-reduce in   möglich ist, da Reduktion und Broadcast jeweils in   möglich sind.

Präfixsumme/Scan

Bearbeiten
 
Informationsfluss von Prefix-Sum/Scan ausgeführt auf drei Einheiten. Der Operator + kann ein beliebiger assoziativer Operator sein.

Das Muster Präfixsumme oder Scan wird genutzt, um Daten oder partielle Resultate mehrerer Prozessoreinheiten zusammenzutragen und mittels eines Operators   Zwischenergebnisse zu berechnen.[6] Die Zwischenergebnisse werden auf den einzelnen Prozessoreinheiten gespeichert. Die Präfixsumme kann als Generalisierung des Musters Reduktion (#Reduktion) aufgefasst werden. Wie in Reduktion und All-reduce (#All-reduce) wird vom Operator   mindestens Assoziativität gefordert, wobei manche Algorithmen zusätzlich Kommutativität erfordern. Häufige Operationen sind  .

Nach Abschluss der Präfixsumme enthält Prozessoreinheit   die Nachricht   . Im Sonderfall der exklusiven Präfixsumme wird stattdessen    berechnet. Manche Algorithmen fordern zudem, dass zusätzlich zur Präfixsumme auch die vollständige Summe auf jeder Prozessoreinheit gespeichert wird, dass also Präfixsumme und All-reduce kombiniert werden.

Für kurze Nachrichten kann eine optimale Implementierung durch eine Hyperwürfel-Topologie erreicht werden. Für lange Nachrichten ist der Hyperwürfel nicht effektiv, da alle Prozessoreinheiten in jedem Schritt aktiv sind und dadurch Pipelining nicht angewendet werden kann. Für lange Nachrichten ist stattdessen ein Binärbaum in Kombination mit Pipelining besser geeignet. Dabei wird die Präfixsumme in eine Aufwärts- und eine Abwärts-Phase zerlegt. Die Reduktion findet in der Aufwärts-Phase statt. Die Abwärts-Phase ist ähnlich zum Broadcast (#Broadcast). Dabei wird die Präfixsumme berechnet, indem die Knoten je unterschiedliche Daten zu ihren linken und rechten Knoten gesendet werden. Pipelining wird wie bei Reduktion und Broadcast angewendet.

Auf einem Binärbaum ist Präfixsumme in Zeit   möglich.

Barriere

Bearbeiten

Die Barriere ist eine Verallgemeinerung des Konzepts der Barriere auf verteiltem Rechnen.[7] Wenn eine Prozessoreinheit die Barriere aufruft, dann wartet sie, bis alle anderen Prozessoreinheiten ebenfalls Barriere aufgerufen haben, before sie im Programm fortfährt. Die Barriere ist also eine Möglichkeit der globalen Synchronisation.

Eine Möglichkeit, die Barriere zu implementieren ist es, All-reduce (#All-reduce) mit einem leeren Operanden aufzurufen. Dadurch wird die Nachrichtengröße   auf einen konstanten Faktor reduziert und nur der Latenz-Term in der Laufzeitbetrachtung bleibt übrig. Da die Laufzeit für All-reduce   ist, liegt die Laufzeit der Barriere also in  .

 
Informationsfluss von Gather ausgeführt auf drei Einheiten.

Das Muster Gather wird genutzt, um Daten von allen Prozessoreinheiten zu sammeln und auf einer einzelnen Prozessoreinheit zusammenzuführen.[8] Liegt zu Beginn die Nachricht   auf Prozessoreinheit  , so soll nach dem Gather auf der Wurzel   die Nachricht   gespeichert werden. Konzeptionell entspricht Gather der Reduktion (#Reduktion), wobei der Operator die Konkatenation der Nachrichten ist. Konkatenation ist assoziativ und erfüllt damit die Voraussetzung der Reduktion.

Durch die Nutzung des Binomialbaum-Algorithmus der Reduktion wird eine Laufzeit von   erreicht. Die Laufzeit ist ähnlich zur Laufzeit   der Reduktion, bis auf einen zusätzlichen Faktor   der an den Term   multipliziert wurde. Dieser Faktor kommt daher, dass die Größe der Nachrichten in jedem Schritt zunimmt. Dies ist durch die Konkatenation als Operator bedingt und steht im Gegensatz zu Operatoren wie  , die eine konstante Nachrichtengröße über alle Schritte bedingen.

All-gather

Bearbeiten
 
Informationsfluss von All-Gather ausgeführt auf drei Einheiten.

Das Muster All-gather wird genutzt, um Daten aller Prozessoreinheiten auf allen Prozessoreinheiten zu sammeln.[8] Gegeben Nachricht   auf Prozessoreinheit  , soll die Nachricht   auf alle Prozessoreinheiten transferiert werden.

All-gather kann auf verschiedene Arten betrachtet werden. Einerseits entspricht es dem Muster All-reduce mit der Operation Konkatenation, so wie Gather als Reduce mit Konkatenation gesehen werden kann. Andererseits entspricht es dem Muster Gather mit anschließendem Broadcast der aggregierten Nachricht mit Größe  . Wir sehen, dass All-gather in Laufzeit   durchgeführt werden kann.

 
Informationsfluss von Scatter ausgeführt auf drei Einheiten.

Das Muster Scatter wird eingesetzt, um Daten einer Prozessoreinheit auf alle Prozessoreinheiten aufzuteilen.[9] Es unterscheidet sich vom Broadcast insofern, als dass nicht alle Prozessoreinheiten die gleiche Nachricht erhalten. Stattdessen erhält jede Prozessoreinheit einen Ausschnitt. Es soll also die auf der Wurzel vorliegende Nachricht   so verteilt werden, dass anschließend auf Prozessoreinheit   die Nachricht   vorliegt. Scatter kann als invertierter Gather (#Gather) gesehen werden.

Für Scatter lassen sich die gleichen Überlegungen wie für Gather anstellen. Das Resultat ist eine Laufzeit in  .

All-to-all

Bearbeiten

Das Muster All-to-all stellt das allgemeinste Kommunikationsmuster dar.[10] Für   ist   die Nachricht, die zu Beginn auf Prozessoreinheit   vorliegt und nach der Operation auf Prozessoreinheit   liegt. Es hat also jede Prozessoreinheit individuelle Nachrichten für alle anderen Prozessoreinheiten. Alle anderen Muster, die keine Operation benötigen, lassen sich durch All-to-all ausdrücken. Beispielsweise kann Broadcast emuliert werden, bei dem die Wurzel   die Nachricht   verteilt, indem   gesetzt wird und   leere Nachricht für  .

Sofern das Netzwerk als vollständiger Graph gesehen werden kann, ist eine Laufzeit in   möglich. Dabei wird All-to-all durch   Runden paarweisen Nachrichtenaustauschs implementiert. Falls   eine Zweierpotenz ist, kann dazu in Runde   Knoten   mit Knoten  , kommunizieren.

Falls die Nachrichtengröße klein ist und die Latenz die Laufzeit dominiert, kann durch einen Hyperwürfel eine Laufzeit in   erreicht werden.

 
Informationsfluss von All-to-All ausgeführt auf drei Einheiten. Buchstaben indizieren Einheiten und Nummern indizieren Informationselemente.

Laufzeitüberblick

Bearbeiten

Diese Tabelle gibt einen Überblick über die bestmöglichen asymptotischen Laufzeiten, sofern die Wahl der Netzwerktopologie frei ist.[11]

Beispieltopologien für eine optimale Laufzeit sind je nach Algorithmus Binärbaum, Binomialbaum und Hyperwürfel.

In der Praxis müssen die Algorithmen an die tatsächlich verfügbaren Topologien angepasst werden, beispielsweise Fat tree, Gitter, Dragonfly.

Bei einigen Operationen kann die Wahl des optimalen Algorithmus von der Eingabegröße   abhängen. Beispielsweise ist Broadcast für kurze Nachrichten optimal auf einem Binomialbaum, während für lange Nachrichten Kommunikation, die Pipelining verwendet, auf einem Binärbaum optimal ist.

In der Tabelle steht in der Spalte Name der Name des jeweiligen Musters. Die Spalte # Sender listet die Anzahl Prozessoreinheiten, die initial eine zu verteilende Nachricht haben. # Empfänger listet die Anzahl Knoten, die eine Nachricht zu empfangen haben. # Nachrichten zeigt die Anzahl Nachrichten, die insgesamt auszuliefern sind. Berechnung listet, ob zusätzlich zur Kommunikation noch eine Berechnung stattfindet. Laufzeitkomplexität listet die asymptotische Laufzeit einer optimalen Implementierung unter freier Wahl der Topologie.

Name # Sender # Empfänger # Nachrichten Berechnung Laufzeitkomplexität
Broadcast       nein  
Reduktion       ja  
All-reduce       ja  
Präfixsumme/ Scan       ja  
Barriere       nein  
Gather       nein  
All-gather       nein  
Scatter       nein  
All-to-all       nein   oder  

Literatur

Bearbeiten
  • Peter Sanders, Kurt Mehlhorn, Martin Dietzfelbinger, Roman Dementiev: Sequential and Parallel Algorithms and Data Structures – The Basic Toolbox. Springer Nature Switzerland AG, 2019, ISBN 978-3-03025208-3.

Einzelnachweise

Bearbeiten
  1. Intercommunicator Collective Operations. The Message Passing Interface (MPI) standard, chapter 7.3.1. Mathematics and Computer Science Division, Argonne National Laboratory.
  2. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 395
  3. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 396–401
  4. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 402–403
  5. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 403–404
  6. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 404–406
  7. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 408
  8. a b Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 412–413
  9. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 413
  10. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 413–418
  11. Sanders, Mehlhorn, Dietzfelbinger, Dementiev 2019, S. 394