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

Post on 06-Apr-2016

221 views 4 download

transcript

Algorithmische Skelette

Michael BrulandMichael Hüllbrock

Münster, den 12.06.03

2

Gliederung(1) Motivation(2) Grundlegende Technologien(3) Algorithmische Skelette(4) Alternativen zur Implementierung mittels

Bibliothek(5) 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(1) Motivation(2) Grundlegende Technologien(3) Algorithmische Skelette(4) Alternativen zur Implementierung mittels

Bibliothek(5) 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

babamap )(:

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

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:

2.3 Partielle Applikationen

)...)...(( 21 tttt n tttt n ),...,,( 21

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

44434241

34333231

24232221

14131211

xxxxxxxxxxxxxxxx

2221

1211

xxxx

2423

1413

xxxx

4241

3231

xxxx

4443

3433

xxxx

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(1) Motivation(2) Grundlegende Technologien(3) Algorithmische Skelette(4) Alternativen zur Implementierung mittels

Bibliothek(5) 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

2221

1211

xxxx

4241

3231

xxxx

4443

3433

xxxx

2423

1413

xxxx

),...,( 4411 xx

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

2221

1211

xxxx

iA

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

Initial Farmer Final

AtomicWorker

AtomicWorker

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)Beispiel n Skelette MPI Quotient

Matrix Multiplikation 1024 35.203 29.772 1.18Kürzester Pfad 1024 393.769 197.979 1.99

Gauss’sches Eliminationsverfahren

1024 13.816 9.574 1.44

FFT 218 2.127 1.295 1.64

Samplesort 218 1.599 † -

Quelle: Kuchen 2002

38

3.6 Laufzeitverhalten (3)Beispiel n Skelette MPI Quotient

Matrix Multiplikation 1024 8.624 6.962 1.24

Kürzester Pfad 1024 93.825 44.761 2.10

Gauss’sches Eliminationsverfahren

1024 7.401 4.045 1.83

FFT 218 0.636 0.403 1.58

Samplesort 218 0.774 † -

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 shapeA.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;}

51

3.7.2 Matrixmultiplikation (4)int main(int argc, char **argv){

try{InitSkeletons(argc,argv));int sqrtp=(int) (sqrt(sk_numprocs)+0.1);DistributedMatrix<int> A(Problemsize,Problemsize,

& add, sqrtp, sqrtp);DistributedMatrix<int> B(Problemsize,Problemsize,

& add, sqrtp,sqrtp);DistributedMatrix<int> C=matmult(A,B);C.show();TerminateSkeletons();}

catch(Exception&){cout << “Exception” << endl << flush;};}

52

Gliederung(1) Motivation(2) Grundlegende Technologien(3) Algorithmische Skelette(4) Alternativen zur Implementierung mittels

Bibliothek(5) Fazit

53

4 Alternativen zur Bibliothek P3L SkIE

54

4.1 P3L (1) Pisa Parallel Programming Language Basiert auf C++ Skelette farm, map, pipe und loop sind vordefiniert

<skelettname> <bezeichner> in(<par>) out(<par>) <prozedur> in(<p>) out(<p>)end <skelettname>

55

4.1 P3L (2)farm myfarm in(int inputA, int inputB) out(int

output) p in(inputA,inputB) out(output)end farm

Anzahl der Worker wird vom Compiler selbstständig ermittelt

Compiler versucht den Speedup zu maximieren „Mapping Problem“ wird durch implementation

templates gelöst

56

4.1 P3L (3)Ausführung in 3 Stufen:

1. Emitter empfängt Datenstrom und verteilt ihn an die Worker

2. Worker führen Prozesse aus und geben das Ergebnis an den Collector

3. Collector empfängt Ergebnisse und schreibt sie in den Ausgabekanal

57

4.2 SkIE (1) Skeleton-based Integrated Enviroment

Vorteile gegenüber P3L Breitere Sprachunterstützung (C, C++, F90, Java,

…) Einbindung von MPI und HPF Grafische Benutzeroberfläche (VisualSkIE) Analysetools

58

4.2 SkIE (2)

59

4.2 SkIE (3)Anwendungsentwicklung mit SkIE in 3 Phasen:

Phase 1: Generieren des Codes und globale Optimierungen

Phase 2: Debugging

Phase 3: Performanzanalyse

60

Gliederung(1) Motivation(2) Grundlegende Technologien(3) Algorithmische Skelette(4) Alternativen zur Implementierung mittels

Bibliothek(5) Fazit

61

5 Fazit (1) Skelette befreien den Programmierer von

Problemen der parallelen Hardware Portierbarkeit von Programmen Globale Sichtweise bei Konzeption und

Programmierung Kommunikationsprobleme wie Deadlock oder

Starvation können nicht vorkommen Integration einer Bibliothek in eine bekannte

Sprache überbrückt „Berührungsängste“

62

5 Fazit (2) Vermeidung von Lernkurveneffekten durch Nutzung

bekannter Programmiersprachen Kostenabschätzungen möglich Performanzeinbußen sind im akzeptablem Rahmen

63

Fragen / Diskussion