Parallele Matrizenmultiplikation

Aus testwiki
Zur Navigation springen Zur Suche springen

Die Multiplikation von Matrizen ist Bestandteil vieler Algorithmen zur Lösung komplexerer Problemstellungen. So wird diese zur Berechnung der Pfadlänge in Graphen genutzt oder zur Bestimmung erreichbarer Knoten verwendet. Hierbei wird die Multiplikation häufig mehrmals ausgeführt, sodass es ein Bestreben ist, die Laufzeit für die Multiplikation zu verringern. Zusätzlich werden Datensätze immer größer, beispielsweise Abhängigkeitsgraphen zwischen Nutzern sozialer Plattformen, wodurch die Größe der Matrizen wächst und effizientere Algorithmen benötigt werden. Neben der Möglichkeit, Algorithmen mit besserer Komplexität zu entwickeln, kann die Multiplikation parallel, also auf mehreren Prozessoren gleichzeitig, ausgeführt werden. Ein Beispiel hierfür ist die Matrizenmultiplikation nach Cannon, entwickelt von Lynn Elliot Cannon.

Algorithmen

Fox-Algorithmus

Der Fox-Algorithmus, benannt nach Geoffrey C. Fox, ist ein Algorithmus zur parallel, auf p Prozessoren gleichzeitig ausgeführten Matrizenmultiplikation. In der Entwicklung des Algorithmus wurde eine Topologie genutzt, in der die Prozessoren in einem Hyperwürfel angeordnet sind. Als Speichermodell wird das Modell des verteilten Speicher genutzt. Hierbei hat jeder Prozessor seinen eigenen privaten Adressraum und die Kommunikation zwischen Prozessoren ist nachrichtenbasiert. Der Anspruch bei der Entwicklung war es einen effizienten und einfach einzusetzenden Algorithmus, zur Nutzung in wissenschaftlichen Berechnungen, zu erstellen. Des Weiteren war es ein Bestreben einen Algorithmus zu entwickeln, der auch für zukünftige Maschinen mit vielen Prozessoren skaliert. Essentiell für den Algorithmus sind Systolische Arrays. Dieses bezeichnet ein Pipe-Netzwerk, durch welches Datenströme hindurchgetaktet werden.[1]

Beschreibung

Datei:Fox Ablauf.webm Es werden zwei n×n-Matrizen A und B multipliziert. Hierbei sind A und B vollbesetzte Matrizen. Die Elemente der Matrizen werden mit Hilfe der i- und j-Koordinaten in der Matrix auf die Prozessoren, die ebenfalls eine i- und j-Koordinate innerhalb des Hyperwürfel besitzen, verteilt. Somit hat zu Beginn der Berechnung jeder Prozessor ein Element der Matrix A und ein Element der Matrix B.[2] Für Matrizen, bei denen gilt n=p erhält jeder Prozessor einen Eintrag der Matrix A und einen Eintrag der Matrix B. Für Matrizen, bei denen gilt n>p erhält jeder Prozessor eine Teilmatrix der Größe (np)×(np) von A und B.[3] Im Fall der Teilmatrizen führt jeder Prozessor eine eigene Matrizenmultiplikation der Teilmatrizen durch.

Während des Algorithmus werden die Elemente der Matrix A mittels Broadcast in den Prozessorreihen, also allen Prozessoren mit gleicher j-Koordinate, verteilt. Die Elemente der Matrix B werden an den darüber liegenden Prozessor weitergegeben. Bei Prozessoren am oberen Rand werden die Elemente an den Prozessor am unteren Ende weitergegeben.

Bei initialer Startkonfiguration hält jeder Prozessor das Element der Matrix A und B, das die gleichen Werte für die Koordinaten i und j besitzt, wie der Prozessor selbst im Hyperwürfel.

Der Algorithmus besteht aus folgenden Schritten:

  1. Broadcast der Elemente auf der Diagonalen der Matrix A an die Prozessoren mit gleicher j-Koordinate. Anschließend halten alle Prozessoren in der ersten Reihe das Element A00, alle Prozessoren in der zweiten Reihe das Element A11. Dieses Muster setzt sich für alle Prozessoren fort.
  2. Multiplikation des empfangenen Elements der Matrix A mit dem derzeit im Prozessor vorhandenen Element der Matrix B.
  3. Alle Prozessoren reichen ihr derzeitiges Element der Matrix B an den Prozessor in der gleichen Spalte, eine Stelle über sich, weiter. Die Prozessoren am Rand übergeben ihr Element an die Prozessoren am unteren Rand.
  4. Broadcast der Elemente auf der 'Diagonalen + 1' der Matrix A an die Prozessoren mit gleicher j-Koordinate. Anschließend halten alle Prozessoren in der ersten Reihe das Element A01, alle Prozessoren in der zweiten Reihe das Element A12. Dieses Muster setzt sich für alle Prozessoren fort.
  5. Multiplikation des empfangenen Elements der Matrix A mit dem derzeit im Prozessor vorhandenen Element der Matrix B und anschließende Addition des Ergebnisses mit dem Ergebnis aus der vorherigen Multiplikation.

Dieses Muster setzt sich fort, bis die Elemente von B wieder in der Ausgangsstellung sind und alle Nebendiagonalen von A verwendet wurden.[4]

Pseudocode

In Pseudocode kann der Algorithmus wie folgt implementiert werden:

FOX(A, B):
1 //Prozessoreinheit(i,j)
2 a:=Aij;
3 b:=Bij;
4 cij:=0;
5 for (l:=0;l<p;l++) {
6    PE(i,(i+l)modp) broadcasts a to PE(i,j) for j0..pj(i+l)modp;
7    cij:=cij+a*b;
8    concurrently {
9        send b to PE((i+1)modp,j);
10   } with {
11       receive b' from PE((i1)modp,j);
12   }
13   b:=b;
14 }

Analyse

Sollen zwei Matrizen der Größe n×n mittels q×q Prozessoren multipliziert werden. So erhält jeder Prozessor zwei Teilmatrizen der Größe (np)×(np).

Das Muster Broadcast-Multiplikation-Rotation wiederholt sich p-mal, bis das Ergebnis feststeht.

Insgesamt hat der Algorithmus eine Berechnungszeit von T=q*(2m3tflop+2m2tcomm+(p1)tstart)

Diese setzt sich zusammen aus den einzelnen Teilschritten:

1. Broadcast der Submatrizen der Matrix A: m2tcomm+(p1)tstart
2. Rotation der Submatrizen der Matrix B um die j-Achse: m2tcomm
3. Multiplikation der Submatrizen und Addition zum vorherigen Teilergebnis: 2m3tflop

Hierbei ist tcomm die Zeit, um eine Gleichkommazahl zwischen Prozessoren zu übertragen, tstart die Startzeit um die Pipeline zu befüllen und tflop die Zeit für eine Multiplikation oder Addition von Gleichkommazahlen.

Die Analyse gilt für alle Maschinen, die der flynnsche Klassifikation MIMD zugeordnet werden können und das Speichermodell des verteilten Speichers haben.[5]

Ein Nachteil des Algorithmus ist die Skalierbarkeit für die Multiplikation von zwei n×n-Matrizen. Es können höchstens n2 Prozessoren benutzt werden, womit die Laufzeit in Ω(n) liegt, da es Θ(n3) Operationen im seriellen Algorithmus sind.[6]

DNS-Algorithmus

Die Grafik zeigt die Matrix A, die rot eingefärbt wurde für eine bessere Wiedererkennung im Video zum Ablauf des DNS-Algorithmus.

Der DNS-Algorithmus, benannt nach Elizier Dekel, David Nassimi und Sartaj Sahni, wurde 1981 von diesen veröffentlicht. Der Algorithmus wurde entwickelt für die parallele Multiplikation von Matrizen auf general purpose Prozessoren. Der Algorithmus richtet sich an parallele Computer die der flynnsche Klassifikation SIMD zugeordnet werden können. Diese Computer haben gemeinsam, dass sie aus p verarbeitenden Elementen (processing elements) PE bestehen. Jedes dieser verarbeitenden Elemente kann die Standard arithmetischen und logischen Operationen durchführen.[7] Insgesamt können bis zu n3 Prozessoren benutzt werden. Diese sind hierbei in einem dreidimensionalen Hyperwürfel angeordnet. Bei n3 skalaren Multiplikationen folgt daraus, dass jeder Prozessor eine skalare Multiplikation durchführen muss.

Der Algorithmus wurde vor allem entwickelt, um Probleme der Graphentheorie zu lösen, beispielsweise das Finden des kürzesten Weges zwischen allen All-Pairs Shortest Path oder das bestimmen von Radius, Durchmesser und Mittelpunkt eines Graphen.[8]

Beschreibung

Es werden zwei

n×n

-Matrizen, A und B multipliziert. Diese sind vollbesetzte Matrizen. Konkret werden bei diesem Algorithmus

m:=p3

und damit

m3

Prozessoren genutzt, die in einem Würfel angeordnet sind. Die Elemente der Matrizen werden mittels der i- und j-Koordinate den Prozessoren mit passenden Koordinaten innerhalb des dreidimensionalen Hyperwürfels zugeordnet. Die Multiplikation soll dann auf

m×m

-Matrizen durchgeführt werden. Die Einträge der Matrizen sind wiederum Matrizen mit der Größe

mn×mn

. Hierbei soll

m

ein Teiler von

n

sein.

Die Grafik zeigt die Matrix B, die zur besseren Wiedererkennung im Video zum Ablauf des DNS-Algorithmus gelb eingefärbt wurde.

Um Prozessoren zu adressieren, nutzen wir die Schreibweise

PE(i,j,k)

. Der initiale Zustand kann daher ausgedrückt werden durch

A(i,k,0),B(0,k,j)

. Es befinden sich die Elemente der Matrix A auf den Prozessoren auf der Vorderseite des Hyperwürfels und die Elemente der Matrix B auf der linken Seitenfläche des Hyperwürfels. Die Prozessoren der linken vorderen Kante haben somit bereits ein Element der Matrix A und ein Element der Matrix B. Dies ist die initiale Startkonfiguration.

Der Algorithmus besteht aus folgenden Schritten:

  1. Broadcast der Matrizen A und B über die n3 Prozessoren. Hierbei werden die Elemente der Matrix A (befindet sich auf der Vorderseite des Würfels) entlang der j-Dimension nach hinten gesendet. Die Elemente der Matrix B (befindet sich auf der linken Außenfläche des Würfels) werden entlang der k-Dimension nach rechts gesendet. Nach Abschluss des Broadcast hält jeder Prozessor 1 Element der Matrix A und ein Element der Matrix B.
  2. Nun führt jeder Prozessor die Multiplikation der vorhandenen Elemente durch.
  3. In der letzten Phase wird die Summe der Einzelergebnisse gebildet. Dies geschieht, indem von oben nach unten die Teilergebnisse gesendet und auf dem Weg addiert werden. Nach Abschluss dieser Operation befindet sich die Ergebnismatrix auf der Bodenfläche des Hyperwürfels.

Pseudocode

Eine Implementierung in Pseudocode könnte wie folgt aussehen.

DNS(A, B):
1 store aik in PE(i,k,1);
2 store bkj in PE(1,k,j);
3 PE(i,k,1) broadcasts aik to PEs(i,k,j) for j1..n;
4 PE(1,k,j) broadcasts bkj to PEs(i,k,j) for i1..n;
5 compute cikj:=aik*bkj on PE(i,k,j);
6 PEs(i,k,j) foreach (k1..n) compute cij:=k=1ncikj to PE(i,1,j);

Datei:Dns Ablauf.webm

Analyse

Angenommen es sollen zwei Matrizen der Größe n×n mittels m:=p3 und damit m3 Prozessoren multipliziert werden. Es gilt die Anzahl Prozessoren p=q3.

Insgesamt hat der Algorithmus hat eine Berechnungszeit von[9]:

  Tp=n3p+tslogp+twn2p(23)logp
  ts steht für die Startup Time. Diese wird benötigt, um eine Nachricht im Sendeprozessor zu handhaben. Hierbei ist die Zeit mitinbegriffen, die benötigt wird um die Nachricht zu erstellen, den Routing-Algorithmus durchzuführen und eine Verbindung zwischen beiden Prozessoren zu etablieren.
  tw steht für die Transferzeit pro Wort. In diesem Fall beispielsweise die Dauer für die Übertragung eines Fließkommazahl.

Die Berechnungszeit setzt sich zusammen aus[9]:

  1 Die Broadcastoperation ausgeführt für beide Matrizen: tslogq+tw(nq)2logq
  2 Die Reduktion für die Ergebnismatrix C: tslogq+tw(nq)2logq
  3 Multiplikation der Teilmatrizen: (nq)3

Hieraus ergibt sich[9]:

   Tp=(nq)3+3tslogq+3tw(nq)2logq

Durch einsetzen der obigen Bedingung p=q3 erhält man die obige Berechnungszeit[9]:

   Tp=n3p+tslogp+twn2p(23)logp

Der Algorithmus ist für n3=Θ(plogp) oder p=Ω(n3logn) kostenoptimal.[10]


Einzelnachweise und Anmerkungen

  1. G.C Fox, S.W Otto, A.J.G Hey: Matrix algorithms on a hypercube I. Matrix multiplication. In: Parallel Computing. Nr. 17, 1987, S. 1.
  2. G.C Fox, S.W Otto, A.J.G Hey: Matrix algorithms on a hypercube I. Matrix multiplication. In: Parallel Computing. Nr. 1, 1987, S. 18.
  3. Vorlage:Literatur
  4. G.C Fox, S.W Otto, A.J.G Hey: Matrix algorithms on a hypercube I. Matrix multiplication. In: Parallel Computing. Nr. 1, 1987, S. 19.
  5. G.C Fox, S.W Otto, A.J.G Hey: Matrix algorithms on a hypercube I. Matrix multiplication. In: Parallel Computing. Nr. 1, 1987, S. 19–21.
  6. Vorlage:Literatur
  7. Eliezer Dekel, David Nassimi, Sartaj Sahni: Parallel Matrix and Graph Algorithms In: SIAM Journal on Computing Nr. 4, 1981, S. 657.
  8. Eliezer Dekel, David Nassimi, Sartaj Sahni: Parallel Matrix and Graph Algorithms In: SIAM Journal on Computing Nr. 4, 1981, S. 660.
  9. 9,0 9,1 9,2 9,3 Vorlage:Literatur
  10. Vorlage:Literatur