1 / 63

Algorithmische Skelette

Algorithmische Skelette. Michael Bruland Michael Hüllbrock Münster, den 12.06.03. Gliederung. Motivation Grundlegende Technologien Algorithmische Skelette Alternativen zur Implementierung mittels Bibliothek Fazit. 1 Motivation (1).

alyssa
Download Presentation

Algorithmische Skelette

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Algorithmische Skelette Michael Bruland Michael Hüllbrock Münster, den 12.06.03

  2. Gliederung • Motivation • Grundlegende Technologien • Algorithmische Skelette • Alternativen zur Implementierung mittels Bibliothek • Fazit

  3. 1 Motivation (1) • „Low-Level-Programmierung“ auf Parallelrechnern häufig erforderlich • Kommunikationsprobleme wie Deadlocks oder Starvation schnell möglich • Einsatz von Programmiersprachen speziell für Parallelrechner erfordert neue Einarbeitung  häufig Scheu vor Nutzung neuer Programmiersprachen  Lernkurveneffekte

  4. 1 Motivation (2) • Portierbarkeit von Programmen erwünscht • Einsatz von Bibliotheken zur Erweiterung bestehender Programmiersprachen • Programmiermuster für Parallelrechner

  5. Gliederung • Motivation • Grundlegende Technologien • Algorithmische Skelette • Alternativen zur Implementierung mittels Bibliothek • Fazit

  6. 2 Grundlegende Technologien • Funktionen höherer Ordnung (Higher-Order-Functions) • Parametrisierte Datentypen • Partielle Applikationen • Verteilte Datenstrukturen

  7. 2.1 Funktionen höherer Ordnung • Gleichstellung von Funktionen und Werten in funktionalen Sprachen • Funktion mit Funktionen und/oder Ergebnissen als Argumenten • Neustrukturierung von Problemen aufgrund allgemeingültiger Berechnungsschemata möglich, durch Funktionsparameter an Kontext anpassbar • Bsp.: wendet eine Funktion auf alle Werte einer Kollektion an

  8. 2.2 Parametrisierte Datentypen • Schablonen von Berechnungsvorschriften • Typen erst durch Übergabe von Parametern in Klassendefinition festgelegt • Überprüfung zur Laufzeit auf Typsicherheit • Implementierung in C++ durch Templates

  9. 2.3 Partielle Applikationen • Funktionen, die mit weniger Argumenten angewendet werden können als eigentlich benötigt • Anwendung auf restliche Argumente führt zum selben Ergebnis wie Auflösen der Ursprungsfunktion • Ermittlung der letzten einstelligen Funktion und Rückgabe an weitere Funktionen • Currying als Identifikation mehrstelliger Funktionen mit einstelligen Funktionen höherer Ordnung • Ausgangsfunktion: • Mit Currying:

  10. 2.4 Verteilte Datenstrukturen (1) • Kollektionen wie Listen, Arrays oder Matrizen • Verteilung auf die partizipierenden Prozessoren • Aufteilung durch verschiedene Verfahren möglich • Blockpartitionierung • Zyklische Partitionierung • …

  11. 2.4 Verteilte Datenstrukturen (2) • Bsp.: Aufteilung einer Matrix auf 4 Prozessoren Prozessor1 Prozessor2 Prozessor3 Prozessor4

  12. 2.5 Eigenschaften von C++ für die Nutzung von Skeletten • Polymorphismus • Partielle Applikationen durch Currying ermöglicht • Parametrisierte Datentypen durch Templates template <class E> class DistributedArray{…}

  13. Gliederung • Motivation • Grundlegende Technologien • Algorithmische Skelette • Alternativen zur Implementierung mittels Bibliothek • Fazit

  14. 3 Algorithmische Skelette (1) • Programmiermuster für Interaktion und Rechenoperationen zwischen Prozessen • Vorimplementierte, parametrisierte Komponenten • Globale Sichtweise bei Implementierung • Entweder Sprachkonstrukte oder Inhalte in Bibliotheken • Realisierung basiert auf MPI • Abstraktion von „Low-Level-Programmierung“ • Hardwareabhängige Implementierung  Portierbarkeit der Programme

  15. 3 Algorithmische Skelette (2) • Aufbau der Bibliothek in C++ • Verteilte Datenstruktur ist Klasse • Nutzung der algorithmischen Skelette durch Methodenaufrufe der Klassen

  16. 3.1 Klassifikation algorithmischer Skelette • Datenparallele Skelette • Rechenskelette • Kommunikationsskelette • Taskparallele Skelette

  17. 3.2 Datenparallele Skelette (1) • Ermöglichen Ortstransparenz • Beherrschung von Datenparallelität • Aufteilung der Daten auf die Prozessoren • Steuerung der Prozessoren, wo welche Daten bearbeitet werden sollen • Bsp.: map oder fold

  18. 3.2 Datenparallele Skelette (2) • Bsp.: Geometrisch • Daten werden partitioniert und auf die Prozessoren verteilt • Kommunikation zwischen benachbarten Prozessoren möglich • Ergebnisse werden von einem Prozess geordnet • Anwendung: Vektorberechnung

  19. 3.2 Datenparallele Skelette (3) Nach Campbell 1996

  20. 3.2.1 Rechenskelette (1) • Arbeiten Elemente einer verteilten Datenstruktur parallel ab • Map: wendet eine Funktion auf Teile einer verteilten Datenstruktur an • Fold: kombiniert alle Elemente einer verteilten Datenstruktur sukzessive mit einer Verknüpfungsfunktion h

  21. 3.2.1 Rechenskelette (2) • Bsp.: Fold • Verknüpfungsfunktion h ist E plus(E,E) • A ist eine verteilte (4x4)-Matrix • A.fold(plus) bildet die Summe über alle Elemente

  22. 3.2.2 Kommunikationsskelette (1) • Tauschen Partitionen einer verteilten Datenstruktur aus • Realisierung basiert auf MPI • Kein Austausch individueller Nachrichten erlaubt → Probleme wie Deadlock, Starvation etc. werden verhindert

  23. 3.2.2 Kommunikationsskelette (2) • Bsp.: A.permutePartition(f) Partition (an Prozessor i) wird an Prozessor f(i) gesendet • Weiteres Kommunikationsskelett: rotate

  24. 3.3 Taskparallele Skelette (1) • Verarbeiten Strom von Eingabewerten in Menge von Ausgabewerten • Teilen den Prozessoren Tätigkeiten zu • Tätigkeit kann Funktion oder wiederum Skelett sein  Verschachtelung von Skeletten möglich • Kann mit Funktion oder partieller Applikation aufgerufen werden

  25. 3.3 Taskparallele Skelette (2) • Bsp.: Farm • Anzahl der Prozessoren ist gleich Anzahl der Worker • Auswahl vom Farmer nicht-deterministisch Quelle: Kuchen 2002

  26. 3.3 Taskparallele Skelette (3) • Initial-Prozess template <class O> class Initial: public Process{ public: Initial(O* (*f)(Empty)) void start() }

  27. 3.3 Taskparallele Skelette (4) • Farm-Prozess template<class I, class O> class Farm: public Process{ public: Farm(Process& worker, int n) void start() }

  28. 3.3 Taskparallele Skelette (5) • Final-Prozess template <class I> class Final: public Process{ public: Final(void(*f)(I)) void start() }

  29. 3.3 Taskparallele Skelette (6) • Bsp.: Divide and Conquer • Probleme werden rekursiv in Subprobleme unterteilt • Lösung der Subprobleme erfolgt unabhängig von einander und parallel • Je nach Implementierung • unterschiedliche Anforderungen an Struktur • Unterstützung von konservativer und spekulativer Parallelität • Anwendung: Quicksort, Branch and Bound

  30. 3.3 Taskparallele Skelette (7) Nach Campbell 1996

  31. 3.3 Taskparallele Skelette (8) • Bsp.: Branch and Bound • Anwendung: Optimierungsprobleme • Vorgehensweise: • n Worker-Kopien durch Konstruktoraufruf • Verknüpfung mit internem Controller • Teillösungen werden vom Controller im Heap gesammelt, falls besser als bestehende • Suboptimale Lösungen werden verworfen

  32. 3.3 Taskparallele Skelette (9) • Bsp.: Branch and Bound template <class I> class BranchAndBound:public Process{ public: BranchAndBound(Process& worker, int n, bool (* lth)(I,I), bool (* isSolution)(I)) void start() }

  33. 3.4 Das 2-Ebenen-Modell (1) • Modell zur Kombination von task- und datenparallelen Skeletten • äußere Ebene: miteinander verzahnte taskparallele Skelette • innere Ebene: sequentielle Programme und datenparallele Skelette

  34. 3.4 Das 2-Ebenen-Modell (2) • Aufgabe: Ein Musikstück soll • von Hintergrundrauschen befreit werden, • Hall hinzugefügt werden, • in ein best. Dateiformat (z.B. wav) konvertiert werden • Lösung mit 2-Ebenen-Modell: • Äußere Ebene : Pipeline • Innere Ebene : sequentielle Bearbeitung oder datenparalleles Skelett

  35. 3.5 Zusätzliche Funktionen • Keine Skelette • Flexible Optimierung des Quellcodes • Lokale und globale Sichtweise möglich • Beispiele: • getLocalRows() gibt die Anzahl der lokal verfügbaren Zeilen zurück • getRows() gibt die Anzahl der Zeilen der gesamten verteilten Matrix zurück • isLocal (int i, int j) ist wahr, wenn das Element mit dem Index i,j lokal verfügbar ist • …

  36. 3.6 Laufzeitverhalten (1) • Skelette sind ein abstraktes Konstrukt • Wie hoch sind die Performanzeinbußen von Skeletten gegenüber einer „reinen“ MPI Implementierung? • Vergleich von 5 Beispielprogrammen auf einer Siemens hpcLine mit 4 bzw. 16 Prozessoren

  37. 3.6 Laufzeitverhalten (2) Quelle: Kuchen 2002

  38. 3.6 Laufzeitverhalten (3) Quelle: Kuchen 2002

  39. 3.6 Laufzeitverhalten (4) Fazit Laufzeitverhalten: • Skelette sind um den Faktor 1,2 bis 2,1 langsamer als „reines“ MPI • Grund: • Overhead bei der Parameterübergabe • Fehlende Optimierungsroutinen

  40. 3.7 Beispiele mit algorithmischen Skeletten • 3.7.1 Gauß‘sches Eliminationsverfahren • 3.7.2 Matrixmultiplikation

  41. 3.7.1 Gauß (1) Eliminationsverfahren nach Gauß • Lösungsmenge und Rang einer n x (n+1) Matrix • Hier: zusätzliche Voraussetzung a1,1 ≠ 0 • Idee: Reduzierung der Variabeln durch Addition/Subtraktion der einzelnen Zeilen mit der Pivotzeile

  42. 3.7.1 Gauß (2) #include „Skeleton.h“ inline double init(const int a, const int b){ return (a==b) ? 1.0 : 2.0;} inline double copyPivot(const DistributedMatrix<double>&A, int k, int i, int j, double Pij){ return A.isLocal(k,k) ? A.get(k,k) : 0;}

  43. 3.7.1 Gauß (3) inline void pivotOp(const DistributedMatrix<double>& Pivot, int rows,int firstrow, int k, double** A){ double Alk; for (int l=0; l<rows; l++){ Alk = A[l][k]; for (int j=k; j<=Problemsize; j++) if (firstrow+1 == k) A[l][j] = Pivot.getLocalGlobal(0,j); else A[l][j] -= Alk * Pivot.getLocalGlobal(0,j);}}

  44. 3.7.1 Gauß (4) void gauss(DistributedMartix<double>& A){ DistributedMatrix<double> Pivot(sk_numprocs,Problemsize+1,0.0,sk_numprocs,1); for (int K=0; k<Problemsize; k++){ Pivot.mapIndexInPlace(curry(copyPivot)(A)(k)); Pivot.broadcastPartition(k/A.getLocalRows(),0); A.mapPartitionInPlace(curry(pivotOp)(Pivot,A.getLocalRows(); A.getFirstRow(),k));}}

  45. 3.7.1 Gauß (5) int main(int argc, char **argv){ try{ InitSkeletons(argc, argv); DistributedMatrix<double> A(Problemsize,Problemsize+1,&init,sk_numprocs, 1); gauss(A); A.show(); TerminateSkeletons();} catch(Exception&){cout << “Exception” << endl <<flush;} }

  46. 3.7.1 Gauß (6) • Ausführung auf 3-Prozessor-Maschine • Pivot.mapIndexInPlace(curry(copyPivot)(A)(k)) kopiert die Pivotzeile (I) in eine p x (n+1), also eine 3x4 Matrix • broadcastPartition übermittelt diese Zeile weiter • Mit mapPartitionInPlace wird auf jeder Partition der Matrix parallel pivotOP ausgeführt

  47. 3.7.1 Gauß (7) • die Zeile II wird mit -(9/6 * I) addiert und die Zeile III mit –(3/6 * I) • Weitere Schritte analog

  48. 3.7.2 Matrixmultiplikation (1) Matrixmultiplikation • Idee: Multiplikation zweier verteilter Matrizen A und B durch Blockpartitionierung und Aufteilung auf n Prozessoren

  49. 3.7.2 Matrixmultiplikation (2) #include „Skeleton.h“ #include „math.h“ inline int negate(const int a) {return –a;} inline int add(const int a, const int b) {return a+b;} template <class C> C sprod (const DistributedMatrix<C>& A, const DistributedMatrix<C>& B, int i, int j, C Cij){ C sum=Cij; for(int k=0;k<A.getLocalRows();k++) sum+=A.getGlobalLocal(i,k))*B.getLocalGlobal(k,j); return sum;}

  50. 3.7.2 Matrixmultiplikation (3) template <class C> DistributedMatrix<C> matmult(DistributedMatrix<C> A,DistributedMatrix<C> B){ //assumption: A, B have same square shape A.rotateRows(& negate); B.rotateCols(& negate); DistributedMatrix<C> R(A.getRows(),A.getCols(),0, A.getBlocksInCol(),A.getBlocksInRow()); for(int i=0;i<A.getBlocksInRow();i++){ R.mapIndexInPlace(curry(sprod<C>)(A)(B)); A.rotateRows(-1); B.rotateCols(-1);} return R;}

More Related