Programmieren mit MPI

Programmieren mit MPI Dr. Victor Pankratius David J. Meder IPD Tichy – Lehrstuhl für Programmiersysteme KIT – die Kooperation von Forschungszentrum K...
Author: Timo Heinrich
5 downloads 2 Views 899KB Size
Programmieren mit MPI Dr. Victor Pankratius David J. Meder IPD Tichy – Lehrstuhl für Programmiersysteme

KIT – die Kooperation von Forschungszentrum Karlsruhe GmbH und Universität Karlsruhe (TH)

Programmieren mit MPI (1) MPI steht für: Message Passing Interface Standard spezifiziert die Schnittstelle einer Bibliothek für Nachrichtentausch. Standardisierung durch ein Gremium bestehend aus: Hardware-Hersteller (Supercomputer, Kommunikationshardware), Software-Firmen Universitäten Anwender

Sehr verbreitet

2

Kapitel 10 ‒ Programmieren mit MPI

Programmieren mit MPI (2) Zumeist kommt eine der beiden frei verfügbaren Implementierungen LAM oder MPIch zum Einsatz. Je nach verwendeter Hardware (Myrinet, Infiniband, etc.) werden angepasste oder erweiterte Versionen verwendet.

LAM wird aktuell nicht weiterentwickelt. Aus dem LAM-Projekt ist das Projekt Open MPI hervorgegangen. Open MPI ist eine MPI-2 Implementierung.

3

Kapitel 10 ‒ Programmieren mit MPI

Programmiermodell P

P

P

P

P

P

P

M

M

M

M

M

M M

Netzwerk Rechnerbündel mit verteiltem Speicher SPMD: Single Program Multiple Data Dasselbe Programm läuft auf allen Knoten. Programm ist in einer sequentiellen Sprache geschrieben (C, C++, Fortran). Alle Variablen sind prozessor- bzw. prozesslokal. Kommunikation findet stets über Bibliotheksaufrufe statt (Nachrichtenaustausch). 4

Kapitel 10 ‒ Programmieren mit MPI

Hello-World in MPI #include #include "mpi.h" int main (int argc, char **argv) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); printf("Hello World.\n My rank is %d (out of %d)\n", rank, size); MPI_Finalize(); return 0; }

Übersetzen: mpicc -o hello hello.c Aufruf: mpirun -np 4 ./hello 5

Kapitel 10 ‒ Programmieren mit MPI

Ausführung von MPI-Programmen (1) #include #include "mpi.h" int main (int argc, char **argv) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); if (rank == 0) { printf(“Hello Universe.\n); } else { printf(“Hello World.\n); } MPI_Finalize(); return 0; }

Übersetzen: mpicc -o hello2 hello2.c Aufruf: mpirun -np 4 ./hello2 6

Kapitel 10 ‒ Programmieren mit MPI

Ausführung von MPI-Programmen (2) int main (int argc, char **argv) { // ... if (rank == 0) { printf(“Hello Universe.\n); } else { printf(“Hello World.\n); } // ... }

7

Kapitel 10 ‒ Programmieren mit MPI

0

1

2

3

Übersetzen und Ausführen von MPIAnwendungen Übersetzen: mpicc (C-Code) oder mpiCC (C++-Code). mpicc und mpiCC sind Skripte, welche die auf dem Rechner

installierten C/C++-Übersetzer aufrufen.

Ausführen: mpirun Argument -np x : Zu verwendende Anzahl x an Prozessoren Weitere Parameter, z.B. -gdb: Startet den ersten Prozess unter dem gdb-Debugger

Beispiel: > mpirun –gdb -np 6 mmult

8

Kapitel 10 ‒ Programmieren mit MPI

Nachrichtenaustausch Nachrichten sind Datenpakete, die zwischen den Prozessen einer parallelen Anwendung ausgetauscht werden. Das Kommunikationssystem benötigt dazu folgende Informationen: Senderkennung Speicherort der Quelldaten Datentyp (Integer, Float, strukturierte Daten, …) Anzahl der Datenelemente beim Sender Empfängerkennung (ein oder mehrere Empfänger) Speicherort der Zieldaten Größe des Empfangsbereiches

9

Kapitel 10 ‒ Programmieren mit MPI

Adressierung (1) MPI verwendet zur Adressierung seiner Prozesse sog. Kommunikatoren. Ein Kommunikator ist eine eindeutige Kommunikationsumgebung (Nachrichtenkontext) für eine Prozessgruppe, deren Elemente von 0 bis N-1 durchnummeriert sind. Der vordefinierte Standardkommunikator ist MPI_COMM_WORLD, der alle gestarteten Prozesse enthält, wohingegen MPI_COMM_SELF nur den Prozess selbst enthält.

1 0

MPI_COMM_WORLD

N-2

10

2

Kapitel 10 ‒ Programmieren mit MPI

N-1

Adressierung (2) Kommunikator = Prozessgruppe + Kontext Bessere Unterstützung von Subroutinen und Bibliotheken bzgl. Koordination mit Hauptprogramm. Beispielszenario: send(to 0) receive(any) call library

call library

call library send(to 0)

receive(from 1) send(to 2) receive(from 0) send(to 1) receive(from 2)

Prozess 0 Hauptprogramm 11

Kapitel 10 ‒ Programmieren mit MPI

Prozess 1

Prozess 2 Subroutine

Adressierung (3)

receive(any) Verzögerung call library ??? receive(from 1) send(to 2)

call library send(to 0) send(to 0) ???

call library

receive(from 2)

Prozess 0

Prozess 1

Hauptprogramm Unterprogramm

Keine Verwendung von Kommunikatoren: Falsche Zuordnung der Nachrichten, evtl. Blockieren. 12

Kapitel 10 ‒ Programmieren mit MPI

receive(from 0) send(to 1)

Prozess 2

Adressierung (4)

receive(any) call library

call library send(to 0)

receive(from 1) send(to 2)

send(to 0)

call library receive(from 2)

Prozess 0

Prozess 1

Kommunikator 1 Kommunikator 2

Verwendung unterschiedlicher Kommunikatoren durch die Anwendung und die Bibliothek. 13

Kapitel 10 ‒ Programmieren mit MPI

receive(from 0) send(to 1)

Prozess 2

Kommunikatoren, Gruppen, Kontexte und virtuelle Topologien Kommunikator: Eindeutige Kommunikationsumgebung. Enthält zwingend einen Kontext und eine Gruppe, evtl. auch eine virtuelle Topologie. Wird in allen Kommunikationsoperationen als Referenz (Handle) verwendet. (Prozess-) Gruppe: Definiert eine geordnete Sammlung von Prozessen + Gültigkeitsbereich der Namen für eine Kommunikation. Wird zur Konstruktion von Kommunikatoren verwendet. Kontext: Definiert eine getrennte eindeutige Umgebung, in der eine Kommunikation isoliert stattfinden kann. Bsp: Trennt Kommunikation von versch. Bibliotheken (Unterscheidung durch Nachrichtenkennung). Virtuelle Topologie: Spezielle Anordnung von Prozessnummern in einer Gruppe, die eine bestimmte Topologie widerspiegeln (s. spätere Folien). 14

Kapitel 10 ‒ Programmieren mit MPI

Kommunikatoroperationen Erfragen der Größe der zugehörigen Prozessgruppe und Ordnungsnummer (Rang) darin: MPI_Comm_size(MPI_Comm comm, int *size) // Größe der Gruppe MPI_Comm_rank(MPI_Comm comm, int *rank) // Rang in der Gruppe

Kommunikator duplizieren (erzeugt neuen Kontext):

MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm)

Neuen Kommunikator erzeugen:

MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm)

Kommunikator teilen:

MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm) Alle Prozesse, die den gleichen Wert für color spezifizieren, sind nach der Operation in derselben Gruppe.

Kommunikator löschen:

MPI_Comm_free(MPI_Comm *comm)

Die Aufrufe zum Erzeugen/Löschen eines Kommunikators müssen immer von allen Prozessen der Prozessgruppe des alten Kommunikators aufgerufen werden.

15

Kapitel 10 ‒ Programmieren mit MPI

Gruppenoperationen Gruppenobjekt aus Kommunikator extrahieren MPI_Comm_group( … ) Größe der Gruppe erfragen MPI_Group_size( … )

Eigenen Rang bestimmen (liefert MPI_UNDEFINED, falls nicht Mitglied) MPI_Group_rank( … )

Gruppe modifizieren

MPI_Group_union( … ) MPI_Group_intersection( … ) MPI_Group_difference( … ) aus vorhandener Gruppe eine neue durch Einfügen MPI_Group_incl( … ) aus vorhandener Gruppe eine neue durch Ausschließen MPI_Group_excl( … )

Die neue Gruppe enthält die Prozesse mit den aufgezählten bzw. den nicht aufgezählten Rangnummern. Gruppe löschen: MPI_Group_free(MPI_Group *group)

Die Funktionen zum Erzeugen neuer Gruppen müssen immer von allen Mitgliedern der alten Gruppe aufgerufen werden!

16

Kapitel 10 ‒ Programmieren mit MPI

Virtuelle Topologien (1) Eine virtuelle Topologie ist eine an einen Kommunikator geknüpfte Zusatzinformation, und zwar die Abbildung von Prozessnummern auf einen am Problem orientierten Namensraum und umgekehrt. Methoden zur Erzeugung / zum Umgang mit virtuellen Topologien: Allgemein: Konstruktion eines Graphen: Knoten sind Prozesse. Kanten sind Kommunikationspfade.

Spezialfall: Kartesische Koordinaten: Ringe, Tori, zwei- oder höherdimensionale Gitter.

17

Kapitel 10 ‒ Programmieren mit MPI

Virtuelle Topologien (2): kartesische Koordinaten MPI_Cart_create(MPI_Comm comm, int ndims, int dims[], int periods[], int reorder, MPI_Comm *newcomm)

0 (0,0)

1 (1,0)

2 (2,0)

3 (3,0)

4 (0,1)

5 (1,1)

6 (2,1)

7 (3,1)

8 (0,2)

9 (1,2)

10 (2,2)

11 (3,2)

ndims=2, dims[]={4,3}, periods[]={true, false} 18

Kapitel 10 ‒ Programmieren mit MPI

Virtuelle Topologien (3): Parameter von MPI_Cart_create() comm: Alter Kommunikator. ndims: Anzahl der Dimensionen des Gitters/Torus. dims: Adresse eines Feldes mit ndims Einträgen, die die Größe

der jeweiligen Dimension angeben. periods: Adresse eines Feldes mit ndims Einträgen, die angeben, ob die jeweilige Dimension periodisch geschlossen ist (1), oder nicht (0). Dies hat Auswirkungen z.B. bei MPI_Cart_shift(), wo bei nicht geschlossenen Dimensionen MPI_PROC_NULL als Kommunikationspartner zurückgegeben werden kann.

reorder: Gibt an, ob MPI die Reihenfolge der Ränge der

beteiligten Prozesse beibehalten soll (0) oder ob es umnummerieren darf, um z.B. eine bessere Abbildung von Prozessen auf die Kommunikationshardware vorzunehmen. newcomm: Adresse des neuen Kommunikators, der dann die virtuelle Topologie enthält (Ausgabeparameter). 19

Kapitel 10 ‒ Programmieren mit MPI

Virtuelle Topologien (4): Nutzung der kartesischen Koordinaten Abbildung der Ordnungsnummer auf Koordinaten MPI_Cart_coords(MPI_Comm comm, int rank, int ndims, int *coords[])

Abbildung der Koordinaten auf die Ordnungsnummer MPI_Cart_rank(MPI_Comm comm, int coords[], int *rank)

Ermittlung des Rangs eines Nachbarprozesses entlang einer bestimmten Dimension, mit Abstand displ MPI_Cart_shift(MPI_Comm comm, int direction, int displ, int *rank_source, int *rank_dest) 20

Kapitel 10 ‒ Programmieren mit MPI

Virtuelle Topologien (5) – Beispiel int main(int argc, char *argv[]) { const int SIZE = 16, UP = 0, DOWN = 1,

LEFT = 2, RIGHT = 3; int taskCount, myRank; int neighbrs[4], dims[2]= {4, 4}, periods[2] = {0,0}, coords[2];

Folgendes Beispiel ermittelt die Ränge der Nachbarn jedes einzelnen Knotens.

MPI_Comm cartcomm; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &taskCount); if (taskCount == SIZE) { MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, 0, &cartcomm); MPI_Comm_rank(cartcomm, &myRank); MPI_Cart_coords(cartcomm, myRank, 2, coords);

MPI_Cart_shift(cartcomm, 0, 1, &neighbrs[UP], &neighbrs[DOWN]); MPI_Cart_shift(cartcomm, 1, 1, &neighbrs[LEFT], &neighbrs[RIGHT]); printf("Rang: %d Koordinaten: %d %d Nachbarn: %d %d %d %d\n", myRank, coords[0], coords[1], neighbrs[UP], neighbrs[DOWN], neighbrs[LEFT], neighbrs[RIGHT]); } MPI_Finalize(); }

21

Kapitel 10 ‒ Programmieren mit MPI

Allgemeines zu Kommunikation (1) Jeweils aus Sicht des aufrufenden Prozesses: Blockierende Kommunikationsoperation die Rückkehr der Kontrolle zum aufrufenden Prozess bedeutet, dass alle Ressourcen (z.B. Puffer), die für den Aufruf benötigt werden, erneut für andere Operationen genutzt werden können. Alle durch den Aufruf ausgelösten Zustandsänderungen des aufrufenden Prozesses finden vor der Rückkehr der Kontrolle statt.

Nicht blockierende Kommunikationsoperation die aufgerufene Kommunikationsanweisung gibt die Kontrolle zurück, bevor die durch sie ausgelöste Operation vollständig abgeschlossen ist und bevor eingesetzte Ressourcen (z.B. Puffer) wieder benutzt werden dürfen.

Die ausgelöste Operation ist erst dann wieder vollständig abgeschlossen, wenn alle Zustandsänderungen dieser Operation für den die Prozedur aufrufenden Prozess sichtbar sind und alle Ressourcen wieder verwendet werden können.

22

Kapitel 10 ‒ Programmieren mit MPI

Allgemeines zu Kommunikation (2) Lokale und nicht-lokale Aufrufe (vgl. MPI-Standard „Semantic Terms“) Lokaler Aufruf einer Prozedur Ein Prozeduraufruf wird lokal genannt, wenn deren Ausführung alleine vom lokal ausgeführten Prozess abhängt.

Nicht-lokaler Aufruf einer Prozedur Abarbeitung hängt von anderen, nicht-lokalen Prozessen ab. Wenn dort eine bestimmte Bedingung nicht eintritt, kehrt der Aufruf unter Umständen nie zurück.

23

Kapitel 10 ‒ Programmieren mit MPI

Allgemeines zu Kommunikation (3) Aus globaler Sicht: Synchrone Kommunikation Die eigentliche Übertragung einer Nachricht findet nur statt, wenn Sender und Empfänger zur gleichen Zeit an der Kommunikation teilnehmen.

Asynchrone Kommunikation Der Sender kann Daten versenden ohne sicher zu sein, dass der Empfänger bereit ist, die Daten zu empfangen.

24

Kapitel 10 ‒ Programmieren mit MPI

Punkt-zu-Punkt-Kommunikation Einfachste Form des Datenaustausches zwischen zwei Prozessen. Genau 2 Prozesse beteiligt: Sender + Empfänger Beide müssen Kommunikationsanweisung explizit durchführen. MPI_Send / MPI_Recv Davon gibt es verschiedene Varianten.

25

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen (1) MPI_Send ( void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) buf: Adresse des Puffers mit den zu

sendenden Daten count: Anzahl der zu sendenden Elemente (nicht die Länge der Daten in Bytes !!) datatype: Typ der Elemente dest: Adresse (Rang) des Empfängers tag: Paketkennung (vom Benutzer frei wählbar) comm: Kommunikator

26

Kapitel 10 ‒ Programmieren mit MPI

MPI_Recv ( void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status) buf: Adresse des Puffers für die zu

empfangenden Daten count: Anzahl der zu empfangenden Datenelemente (nicht die Länge der Daten in Bytes !!) datatype: Typ der Elemente source: Adresse (Rang) des Senders (oder MPI_ANY_SOURCE) tag: Paketkennung (oder MPI_ANY_TAG) comm: Kommunikator status: Statusinformation und Fehlercodes (Rückgabewert)

Senden und Empfangen (2) MPI_Send() und MPI_Recv() Typischerweise blockierende, synchrone Operationen Recv kann gestartet werden, wenn Send noch nicht gestartet Recv blockiert, bis Nachricht erhalten wurde

Send kann gestartet werden, wenn Recv noch nicht gestartet Send blockiert, bis Nachricht gesendet wurde

Das tatsächliche Verhalten des Senders sowie Puffermechanismen hängen aber von der spezifischen MPI-Implementierung ab. Achtung: Fehlerquelle für Verklemmungen (Deadlocks)!

27

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen – Beispiel (1)

MPI_Comm_rank(comm, &myRank);

Warten auf Daten von P1

Quelle / Ziel if (myRank == 0) { /* P0 */ MPI_Recv(recvbuf, count, MPI_INT, MPI_Send(sendbuf, count, MPI_INT, } else if (myRank == 1) { /* P1 */ MPI_Recv(recvbuf, count, MPI_INT, MPI_Send(sendbuf, count, MPI_INT, }

1, tag, comm, &status); 1, tag, comm);

P0 Warten auf Daten von P0

0, tag, comm, &status); 0, tag, comm);

P1

Problem? Beide Prozesse warten gegenseitig aufeinander Keiner sendet  Verklemmung (Deadlock) Parallele und verteilte Programmierung, T.Rauber, G. Rünger, Springer, 2000, S. 173 28

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen – Beispiel (2a) MPI_Comm_rank(comm, &myRank);

Quelle / Ziel if (myRank == 0) { /* P0 */ MPI_Send(sendbuf, count, MPI_INT, MPI_Recv(recvbuf, count, MPI_INT, } else if (myRank == 1) { /* P1 */ MPI_Send(sendbuf, count, MPI_INT, MPI_Recv(recvbuf, count, MPI_INT, }

1, tag, comm); 1, tag, comm, &status);

0, tag, comm); 0, tag, comm, &status);

Problem? Nur korrekt, wenn die abgeschickten Nachrichten aus sendbuf in einem Systempuffer zwischengespeichert werden und die Kontrolle danach sofort wieder an den Sender zurückgegeben wird. Ansonsten tritt eine Verklemmung (Deadlock) auf. Parallele und verteilte Programmierung, T.Rauber, G. Rünger, Springer, 2000, S. 173 29

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen – Beispiel (2b) Mit Systempuffer

Ohne Systempuffer Wartet, bis P1 die Daten entgegennimmt

P0 Send(P1)

P0

Receive(P1)

Send(P1)

Send(P0) Receive(P0)

P1

Aufruf kehrt sofort zurück Es ist sichergestellt, dass ein Prozess nur für ihn bestimmte Nachrichten bekommt. 30

Kapitel 10 ‒ Programmieren mit MPI

Wartet, bis P0 die Daten entgegennimmt

Send(P0)

P1

Aufruf blockiert  Verklemmung

Senden und Empfangen – Beispiel (3) MPI_Comm_rank(comm, &myRank);

Quelle / Ziel

if (myRank == 0) { /* P0 */ MPI_Send(sendbuf, count, MPI_INT, MPI_Recv(recvbuf, count, MPI_INT, } else if (myRank == 1) { /* P1 */ MPI_Recv(recvbuf, count, MPI_INT, MPI_Send(sendbuf, count, MPI_INT, }

4. Receive(P1), Nimmt Daten entgegen

1, tag, comm); 1, tag, comm, &status);

0, tag, comm, &status); 0, tag, comm);

1. Send(P1)

P1

P0

2. Receive(P0), Nimmt Daten entgegen

4. Receive(P1), Nimmt Daten entgegen

2. Send(P1)

P0

3. Send(P0) 3. Receive(P1), Nimmt Daten entgegen

P1

2. Receive(P0), Nimmt Daten entgegen

3. Send(P0)

2. Send(P1)

P0

P1

1. Receive(P0), Nimmt Daten entgegen

P1 4. Send(P0)

1. Receive(P0), Nimmt Daten entgegen

3. Receive(P1), Nimmt Daten entgegen

1. Send(P1)

P0 4. Send(P0)

Parallele und verteilte Programmierung, T.Rauber, G. Rünger, Springer, 2000, S. 173 31

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen – Beispiel (4a) Bei mehr als zwei Prozessen (Gesamtzahl gerade): Prozesse mit ungerader ID senden zuerst an Prozesse mit gerader ID und empfangen anschließend von Prozessen mit gerader ID. Prozesse mit gerader ID empfangen zuerst von Prozessen mit ungerader ID und senden anschließend an Prozesse mit gerader ID. Beispiel:

Parallele und verteilte Programmierung, T.Rauber, G. Rünger, Springer, 2000, S. 174 32

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen – Beispiel (4a) Bei mehr als zwei Prozessen (Gesamtzahl gerade): Prozesse mit ungerader ID senden zuerst an Prozesse mit gerader ID und empfangen anschließend von Prozessen mit gerader ID. Prozesse mit gerader ID empfangen zuerst von Prozessen mit ungerader ID und senden anschließend an Prozesse mit gerader ID. Beispiel:

Prozess 1

Prozess 2

Prozess 3

Prozess 4

MPI_Send zu 2 MPI_Recv von 4

MPI_Recv von 1 MPI_Send zu 3

MPI_Send zu 4 MPI_Recv von 2

MPI_Recv von 3 MPI_Send zu 1

Parallele und verteilte Programmierung, T.Rauber, G. Rünger, Springer, 2000, S. 174 33

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen – Beispiel (4b)

Send()

P1

Send()

Send()

Send()

Receive()

Receive()

Receive()

P2

P3

P4

Receive()

34

Prozess 1

Prozess 2

Prozess 3

Prozess 4

MPI_Send zu 2 MPI_Recv von 4

MPI_Recv von 1 MPI_Send zu 3

MPI_Send zu 4 MPI_Recv von 2

MPI_Recv von 3 MPI_Send zu 1

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen (3) Hello World v2 in MPI #include #include "mpi.h"

0

int main (int argc, char **argv){ int rank, size, i, buf; MPI_Status status;

1

MPI_Init(&argc, &argv);

MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); if(rank == 0){ /* Master process */ for(i = 1; i < size; i++){ MPI_Recv(&buf, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status) printf("Got message from node %d\n",buf); } } else { /* Client process */ MPI_Send(&rank, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); return 0;

Rang des Empfängers

}

35

1

Kapitel 10 ‒ Programmieren mit MPI

2 2

n …

n

Weiteres Beispiel #include #include "mpi.h"

Gegeben n Prozesse Prozess 1 schickt Nachricht an Prozess 2 Jeder i-te Prozess leitet diese weiter an i+1 Prozess n gibt Nachricht aus.

int main( argc, argv ) { int rank, value, size; MPI_Status status; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &rank ); MPI_Comm_size( MPI_COMM_WORLD, &size );

do { if (rank == 0) { scanf( "%d", &value ); MPI_Send( &value, 1, MPI_INT, rank + } else { MPI_Recv( &value, 1, MPI_INT, rank if (rank < size - 1) { MPI_Send( &value, 1, MPI_INT, rank } printf( "Process %d got %d\n", rank, } } while (value >= 0); MPI_Finalize( ); return 0; }

36

Kapitel 10 ‒ Programmieren mit MPI

1, 0, MPI_COMM_WORLD); 1, 0, MPI_COMM_WORLD, &status); + 1, 0, MPI_COMM_WORLD ); value );

Senden und Empfangen (4) Es gibt vier verschiedene Modi für Sendeoperationen: Standard Send (send). Buffered Send (bsend) Synchronous Send (ssend) Ready Send (rsend)

In allen Fällen kann der Sendepuffer wieder verwendet werden, wenn diese Aufrufe zurückkehren.

37

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen (5) Standard Send Allgemeine Sendeoperation Ob die Nachricht bei Standard Send gepuffert wird oder nicht, liegt an der jeweiligen Implementierung. Wird im Sendeknoten gepuffert, kehrt die Sendeoperation sofort zurück (dann ist der Aufruf lokal) Wird nicht gepuffert, muss unter Umständen gewartet werden, bis ein Empfangspuffer beim Empfangsknoten verfügbar wird oder bis der Empfänger seine Empfangsoperation startet. (dann ist der Aufruf nicht-lokal)

Das Verhalten beim Standard Send kann insbesondere auch von der Nachrichtengröße abhängen.

38

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen (6) Gepuffertes Senden Bei einer gepufferten Sendeoperation trägt das MPI-System des Sendeknotens dafür Sorge, dass die Nachricht irgendwo zwischengespeichert wird. Bei bsend() muss der Puffer dem MPI-System vorher durch den expliziten Aufruf von buf_attach() zur Verfügung gestellt werden.

Eine gepufferte Sendeoperation ist eine lokale Operation, d.h. sie wird immer beendet, ganz gleich, ob eine passende Empfangsoperation im Zielknoten vorliegt oder nicht. Ein Pufferüberlauf wird als Fehler behandelt. MPI_System

bsend() Sender

39

Kapitel 10 ‒ Programmieren mit MPI

Puffer

Empfänger

Senden und Empfangen (7) Synchrones Senden Eine synchrone Sendeoperation kehrt erst dann zurück, wenn der Empfänger einen passende Empfangsauftrag abgesetzt hat. Es muss aber noch kein Byte der Nachricht beim Empfänger angekommen sein. ssend: Der Aufruf ist nicht-lokal, d.h. ob (und wann) er

zurückkehrt, hängt vom Verhalten des Senders ab. recv started ?

ssend() proceed ! Sender

40

Kapitel 10 ‒ Programmieren mit MPI

[wait()] recv() Empfänger

Senden und Empfangen (8) Ready Send Einsparung der Abfrage, ob der Sender schon bereit ist Kann zur Leistungsverbesserung verwendet werden Einsparung zweier Rendezvous-Nachrichten

Ready Send entspricht Standard Send, aber das MPI-System des Senders geht davon aus, dass beim Empfänger ein passender Empfangsauftrag bereits vorliegt. Andernfalls ist das Verhalten der Anwendung undefiniert Folgen: Blockieren, Verlust der Nachricht, etc. Es wird dann kein Fehler signalisiert

Prinzipiell kann in einem korrekten Programm jedes Ready Send durch ein Standard Send ersetzt werden, ohne die Semantik zu verändern. 41

Kapitel 10 ‒ Programmieren mit MPI

Senden und Empfangen (9) SendReceive Operation Datenaustausch (gleichzeitiges Senden und Empfangen): MPI_Sendrecv(sendbuf, sendcount, sendtype, dest, sendtag, recvbuf, recvcount, recvtype, source, recvtag, comm, &status);

X

Sender

42

Kapitel 10 ‒ Programmieren mit MPI

Receiver

Weitere Details (1) Auftrennen der Aufrufe zum Senden und Empfangen: Sendeaufruf Empfangsaufruf

→ →

Sendestart + Abschluss Empfangsstart + Abschluss

Zwischen Start und Abschluss der Kommunikationsoperation können andere Aufgaben vom Prozess ausgeführt werden Die Kommunikation wird von MPI (bzw. dem Kommunikationssystem, meist von der Hardware) asynchron ausgeführt.

Beachte: Bei Initiieren der Sendeoperation wird Modus festgelegt isend(), ibsend(), irsend(), issend() Dagegen nur ein Aufruf zum Initiieren der Empfangsoperation: irecv()

unabhängig vom Sendemodus und davon, ob synchron oder asynchron gesendet wurde!

43

Kapitel 10 ‒ Programmieren mit MPI

Weitere Details (2) Die ixsend() und irecv() Aufrufe (mit x = „b“, „s“, „r“ oder „“) selbst sind lokal, d.h. kehren gewöhnlich sofort zurück. Auf die Beendigung einer Operation kann mit MPI_Wait() gewartet werden. Mit MPI_Test() kann überprüft werden, ob die Operation bereits abgeschlossen ist (lokaler Aufruf). Jede asynchrone Operation muss zu Ende geführt werden, d.h. es ist entweder MPI_Test() solange aufzurufen, bis sie abgeschlossen ist, oder

MPI_Wait() aufzurufen.

Auch die gemischte Variante: MPI_Test(), MPI_Test(), …, MPI_Wait() ist möglich.

Bei MPI_Test() und MPI_Wait() wird die gemeinte Operation durch ein Handle identifiziert, das von den ixsend() bzw. irecv() zurückgegeben wird.

44

Kapitel 10 ‒ Programmieren mit MPI

Empfangstests Idee: Schaue nach, ob es eine Nachricht gibt, die einem bestimmten Muster entspricht Danach je nach Status behandeln

Blockierender Empfangstest: MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status) Wartet, bis eine Nachricht verfügbar ist, die mit einem MPI_Recv() mit gleichem source und tag empfangen würde (nicht-lokaler Aufruf).

Nicht blockierender Empfangstest: MPI_Iprobe(int source, int tag, MPI_Comm comm, MPI_Status *status)

Testet, ob eine Nachricht verfügbar ist, die mit einem MPI_Recv() mit gleichem source und tag empfangen würde (lokaler Aufruf). 45

Kapitel 10 ‒ Programmieren mit MPI

MPI Datentypen (für C) MPI_CHAR, MPI_UNSIGNED_CHAR MPI_SHORT, MPI_UNSIGNED_SHORT MPI_INT, MPI_UNSIGNED MPI_LONG, MPI_UNSIGNED_LONG MPI_FLOAT MPI_DOUBLE MPI_LONG_DOUBLE MPI_BYTE MPI_PACKED

abgeleitete Datentypen (engl. derived data types)

46

Kapitel 10 ‒ Programmieren mit MPI

Abgeleitete Datentypen (1) Idee: Ein allgemeiner Datentyp besteht aus zwei Komponenten: einer Sequenz von Basistypen einer Sequenz von Abständen (gerechnet von einer Basisadresse buf an).

Dieses Komponentenpaar wird als Typemap mit der Signatur Typesig bezeichnet: Typemap = {(type0,disp0), … ,(typen-1,dispn-1)} Typesig = {type0, … ,typen-1}

Typemap und Basisadresse buf werden verwendet, um

einen passenden Kommunikationspuffer zu beschreiben. 47

Kapitel 10 ‒ Programmieren mit MPI

Abgeleitete Datentypen (2): „Contiguous“ und „Vector“ MPI_Type_contiguous(int count, MPI_Type oldtype, MPI_Type *newtype) Beispiel: Datentyp für RGB-Farben mit 3 Ganzzahlen MPI_Type_contignous(3, MPI_INT, &RGB_Color); MPI_Type_vector(int count, int blocklength, int stride, MPI_Type oldtype, MPI_Type *newtype) count=2 blocklength=3 stride=5

oldtype newtype

e1

e2

e3

Block 1 48

Kapitel 10 ‒ Programmieren mit MPI

(Anzahl der Blöcke) (Elemente pro Block), (Elemente zwischen dem Anfang von zwei Blöcken) e1

e2

e3 Block 2

Abgeleitete Datentypen (3): MPI_Type_contiguous MPI_Type_contiguous(…) liefert einen Identifikator für einen neuen,

abgeleiteten Datentyp. Der abgeleitete Datentyp besteht aus einer Menge gleicher Elemente eines bereits existierenden Datentyps. Die Elemente sind im Speicher hintereinander angeordnet. Beispiel: Datentyp zum Speichern von 4 Byte Werten (z.B. einer IP-Adresse) MPI_Datatype ipAddress_type; // Identifikator des neuen Typs MPI_Type_contiguous(4, MPI_BYTE, &ipAddress_type); MPI_Type_commit(&ipAddress_type); // Datentyp anmelden // … Datentyp verwenden … MPI_Type_free(&ipAddress_type); // Datentyp freigeben

Anmerkung: Wird der Typ mit MPI_Type_free(…) freigegeben, werden noch laufende Kommunikationen, die diesen Typ verwenden, nicht beeinflusst. Von diesem Typ abgeleitete Typen werden ebenfalls nicht beeinflusst. 49

Kapitel 10 ‒ Programmieren mit MPI

Kollektive Operationen (1) Synchronisation einer Prozessgruppe: MPI_Barrier(MPI_Comm comm) Achtung: Eine Barriere garantiert nur folgendes: Kein Prozess verlässt die Barriere, bevor nicht alle anderen Prozesse die Barriere betreten haben. Das heißt, dass nicht alle Prozesse die Barriere notwendigerweise zum gleichen Zeitpunkt verlassen.

P3

P0

P4

P2 P1

P5

Barriere Zeit

50

Kapitel 10 ‒ Programmieren mit MPI

P0

P1

P2

P3

P4

P5

Kollektive Operationen (2) Broadcast: Verteilen der Daten vom Prozess mit Kennung root zu allen anderen Prozessen in der Gruppe (einschließlich root). Kann von jedem der Prozesse aufgerufen werden. MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm)

Daten

A0

A0 A0

Prozesse

Prozess mit Rang root:

A0 A0 Broadcast

A0 A0

buf 51

Kapitel 10 ‒ Programmieren mit MPI

buf

Kollektive Operationen (3) Scatter: Verteilen von Elementen aus einem Kommunikationspuffer des Prozesses root an verschiedene Prozesse: MPI_Scatter(void *sendbuf, int sendcnt, MPI_Type sendtype,void *recvbuf, int recvcnt, MPI_Type recvtype, int root, MPI_Comm comm)

Der i-te Datenblock aus sendbuf wird an den i-ten Prozess gesendet. Ergebnis dasselbe, als ob root n einzelne Sendeoperationen auf Teile seines Puffers ausgeführt hätte. Daten

A0 A1 A 2 A3 A4 A5

A0 A1

Prozesse

root:

A2 A3 Scatter

A4 A5

sendbuf 52

Kapitel 10 ‒ Programmieren mit MPI

recvbuf

Kollektive Operationen (4) Gather: Aufsammeln von Kommunikationspuffern verschiedener Prozesse: MPI_Gather(void *sendbuf, int sendcnt, MPI_Type sendtype, void *recvbuf, int recvcnt, MPI_Type recvtype, int root, MPI_Comm comm)

Der Sendepuffer des i-ten Prozesses wird an der i-ten Position von recvbuf in root gespeichert. Ergebnis dasselbe, als ob folgende Einzeloperationen ausgeführt worden wären Jeder Prozess (inkl. root) sendet Inhalt seines Sendepuffers an root Der Prozess root empängt alle Nachrichten und speichert sie in der Reihenfolge der Ränge ab

Prozesse

Daten

A0

root:

B0 C0 D0 E0

sendbuf

F0 53

A0 B0 C 0 D 0 E0 F0

Kapitel 10 ‒ Programmieren mit MPI

Gather

recvbuf

Kollektive Operationen – Beispiel Matrixmultiplikation Gegeben: Matrix A, B der Größe N x N Prinzip bei 4 Prozessoren:

A

B

A 0 1 2 3

Scatter A, broadcast B, multipliziere Streifen lokal P0:

B

P1:

B

P2:

B

P3:

B

A0*B -> C A1*B->C A2*B->C A3*B->C

Gather: Setzt Streifen mit Ergebnis zu Matrix C zusammen

54

Kapitel 10 ‒ Programmieren mit MPI

C A0*B -> C A1*B->C A2*B->C A3*B->C

Kollektive Operationen – Beispiel für Scatter, Gather und Broadcast main(int argc, char *argv[]) { Berechne das Produkt MPI_Init (&argc, &argv); Matrizen matrixA und MPI_Comm_rank(MPI_COMM_WORLD, &myRank); MPI_Comm_size(MPI_COMM_WORLD, &commSize); matrixB parallel. MPI_Scatter(matrixA, N*N/commSize, MPI_INT, matrixA, N*N/commSize, MPI_INT, 0, MPI_COMM_WORLD); MPI_Bcast(matrixB, N*N, MPI_INT, 0, MPI_COMM_WORLD); for (i = 0; i < N/commSize; i++) { for (j = 0; j < N; j++) { matrixC[i][j] = 0; for (k = 0; k < N; k++) { matrixC[i][j] += matrixA[i][k] * matrixB[k][j]; } /* Ende k */ } /* Ende j */ } /* Ende i*/ MPI_Gather(matrixC, N*N/commSize, MPI_INT, matrixC, N*N/commSize, MPI_INT, 0, MPI_COMM_WORLD); MPI_Finalize(); }

55

Kapitel 10 ‒ Programmieren mit MPI

der

Kollektive Operationen (5) Allgather: Elemente von allen werden bei allen gesammelt (wie Gather, aber das Ergebnis findet sich hinterher bei jedem Prozess) MPI_Allgather(void *sbuf, int scnt, MPI_Type stype, void *rbuf, int rcnt, MPI_Type rtype, MPI_Comm comm)

Prozesse

Daten

A0

A 0 B 0 C 0 D 0 E 0 F0

B0

A0 B0 C 0 D 0 E0 F0

C0

A0 B0 C 0 D 0 E0 F0

D0

A0 B0 C 0 D 0 E0 F0 Allgather

E0 F0

A0 B0 C 0 D 0 E0 F0

sendbuf 56

A0 B0 C 0 D 0 E0 F0

Kapitel 10 ‒ Programmieren mit MPI

recvbuf

Kollektive Operationen (6) Alltoall: Alle erhalten von allen anderen jeweils ein bestimmtes Element Kann auch durch mehrere Send/Receive-Operationen ausgedrückt werden MPI_Alltoall(void *sbuf, int scnt, MPI_Type stype, void *rbuf, int rcnt, MPI_Type rtype, MPI_Comm comm) Prozesse

Daten

A0 A1 A2 A3 A4 A5

A0 B0 C 0 D 0 E0 F0

B0 B1 B2 B3 B4 B5

A1 B1 C 1 D 1 E1 F1

C0 C1 C2 C3 C4 C5 D0 D1 D2 D3 D4 D5

A2 B2 C 2 D 2 E2 F2 A3 B3 C 3 D 3 E3 F3

E0 E1 E2 E3 E4 E5 F0 F1 F2 F3 F4 F5

sendbuf 57

Kapitel 10 ‒ Programmieren mit MPI

Alltoall

A4 B4 C 4 D 4 E4 F4 A5 B5 C 5 D 5 E5 F5

recvbuf

Reduktionsoperationen (1) Globale Operationen auf verteilten Daten: MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Type datatype, MPI_Op operator, int root, MPI_Comm comm) op = MPI_MAX

op = MPI_SUM root

21

1

58

2

3

4

Kapitel 10 ‒ Programmieren mit MPI

5

root

8

6

1

5

3

8

5

2

Reduktionsoperationen (4) Vordefinierte Operatoren: Maximum, Minimum, Summe, Produkt MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD

Boolesche Operatoren (logische/bitweise Verknüpfung) MPI_LAND, MPI_LOR, MPI_LXOR,

MPI_BAND MPI_BOR MPI_BXOR

Maximum/Minimum inkl. Position MPI_MAXLOC, MPI_MINLOC

(als Datentyp wird ein Paar verwendet)

Es sind auch selbstdefinierte (assoziative) Operatoren möglich.

59

Kapitel 10 ‒ Programmieren mit MPI

Reduktionsoperationen (5) Globale Operationen auf verteilten Daten: MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Type datatype, MPI_Op operator, MPI_Comm comm)

Wie Reduce, aber alle erhalten das Ergebnis.

8

8

8

8

8

8

21

21

21

21

21

21

1

5

3

8

5

2

1

2

3

4

5

6

op = MAX

60

Kapitel 10 ‒ Programmieren mit MPI

op = SUM

Reduktionsoperationen (6) Globale Operationen auf verteilten Daten mit Zwischenergebnissen (Prozess i erhält das Ergebnis einer Reduktionsoperation auf den Prozessen 1...i, also inklusiv) Präfix-Reduktion auf in der Gruppe verteilten Daten MPI_Scan(void *sendbuf, void *recvbuf, int count, MPI_Type datatype, MPI_Op operator, MPI_Comm comm)

61

1

5

1

5

5

8

3 8 op = MAX

Kapitel 10 ‒ Programmieren mit MPI

8

8

1

3

6

10

5

2

1

2

3 4 op = SUM

15

21

5

6

Zur Erinnerung – Numerische Integration P1

P2

P3

P4

1



4.04.0 f(x) = 2 dx =  (1+x ) 2) (1+x

4.0

0

   Pi

2.0

i

0.0 62

X

1.0

Kapitel 10 ‒ Programmieren mit MPI

Beispiel – Berechnung von π (1a) #include #include "mpi.h" int main (int argc , char ** argv) { double width; Folgendes Beispiel berechnet Pi double sum, lsum; näherungsweise. Zum Einsammeln der int intervals, i; Teilergebnisse werden MPI_Send() und int procCount , procRank; MPI_Receive() verwendet. MPI_Status status; /*Initialisierung der MPI-Umgebung */ if (MPI_Init(&argc,&argv) != MPI_SUCCESS) { exit(1); } /* Ermitteln von Größe der Gruppe und des Rangs */ MPI_Comm_size(MPI_COMM_WORLD, &procCount); MPI_Comm_rank(MPI_COMM_WORLD, &procRank);

63

Kapitel 10 ‒ Programmieren mit MPI

Beispiel – Berechnung von π (1b) intervals = atoi(argv[1]); width = 1.0 / intervals; intervalsPerProc = intervals / procCount; // zusätzlich intervalsPerProc für „letzten“ Prozessor anpassen procStart = procRank * intervalsPerProc; lsum = 0; for(i = procStart; i < procStart+intervalsPerProc; i++) { double x = (i+0.5) * width; lsum += 4.0/(1.0+x*x); } lsum *=width; if (procRank!= 0) MPI_Send(&lsum, 1, MPI_DOUBLE, 0, 0 , MPI_COMM_WORLD); else { sum = lsum; for( i = 1; i < procCount; ++i ) { MPI_Recv(&lsum, 1, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status ); sum += lsum; } } MPI_Finalize(); return(0) }

64

Kapitel 10 ‒ Programmieren mit MPI

P1

P2

P3

P4

4.0

2.0

0.0

X

1.0

Beispiel – Berechnung von π (2a) #include #include "mpi.h" int main (int argc , char ** argv) { double width; double sum, lsum; int intervals, i; int procCount , procRank; MPI_Status status;

Folgendes Beispiel berechnet Pi näherungsweise. Zum Einsammeln der Teilergebnisse wird MPI_Reduce() verwendet.

/*Initialisierung der MPI-Umgebung */ if (MPI_Init(&argc,&argv) != MPI_SUCCESS) { exit(1); } /* Ermitteln von Größe der Gruppe und des Rangs */ MPI_Comm_size(MPI_COMM_WORLD, &procCount); MPI_Comm_rank(MPI_COMM_WORLD, &procRank);

65

Kapitel 10 ‒ Programmieren mit MPI

Beispiel – Berechnung von π (2b) intervals = atoi(argv[1]); width = 1.0 / intervals; lsum = 0; for(i = procRank; i < intervals; i += procCount) { double x = (i+0.5) Sendepuffer * width; der

lokalen Variablen lsum, die von allen an root gesendet werden

lsum += 4.0/(1.0+x*x); } lsum *= width;

Ergebnispuffer im Prozess root

MPI_Reduce(&lsum, &sum, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Finalize(); return 0; }

66

Kapitel 10 ‒ Programmieren mit MPI

root aggregiert Werte mit MPI_SUM und schreibt sie in sum

Ausblick: MPI-2 Neue Version des Standards, erweitert MPI-1 in einigen Bereichen: Loslösung vom starren Prozessmodell: Starten weiterer Prozesse zur Laufzeit möglich. Mit den neu gestarteten Prozessen kann kommuniziert werden. Kommunikation mit schon laufenden MPI-Anwendungen möglich Kommunikation über sog. Ports. Finden benannter Ports über eine Registry

Einseitige Kommunikation: Put, Get, Accumulate, Methoden zur Synchronisation Parallele Datei-Ein- und Ausgabeoperation Binding für C++ … weitere Verbesserungen, z.B. bei der Konstruktion von Datentypen.

67

Kapitel 10 ‒ Programmieren mit MPI

MPI-2: Einseitige Kommunikation Mit der kollektiven Operation MPI_Win_create(void* base, …, MPI_Communicator comm, MPI_Win *win)

definiert jeder Prozess aus comm einen Speicherbereich, auf den von den anderen Prozessen aus mit MPI_Put() oder MPI_Get() zugegriffen werden kann. MPI_Put() und MPI_Get() sind einseitige Operationen. Sie werden vom Quellprozess (source) unabhängig vom Zielprozess (target)

ausgeführt. Als Zielprozess wird der Prozess bezeichnet, auf dessen Speicherbereich zugegriffen wird. Synchronisation: Die Aufrufe zur einseitigen Kommunikation müssen von Aufrufen zur Synchronisation eingerahmt werden. Erst nach Abschluss dieser sind alle Datentransfers abgeschlossen, und die Speicherbereiche enthalten gültige Daten bzw. sind wiederverwendbar. 68

Kapitel 10 ‒ Programmieren mit MPI

MPI-2: Einseitige Kommunikation Source

Target

Speicher

Fenster

Fenster

Ablauf: Knoten „source“ greift mittels get() und put() auf das Fenster des Knotens „target“ zu. Für Quell- bzw. Zieldaten können im Prinzip beliebige Speicherbereiche verwendet werden. Knoten „target“ stellt sein Fenster zur Verfügung. Evtl. geänderte Daten sind erst nach Synchronisation sichtbar.

69

Kapitel 10 ‒ Programmieren mit MPI

Neuerungen in MPI 2.2 Aktuell ist MPI in der Version 2.2 vom 4. September 2009. Der Standard enthält unter anderem folgende neue Funktionen: Durchführen von lokalen Reduktionen ohne Kommunikation Zwei neue Varianten von MPI_reduce_scatter: Verteilen von gleich großen Blöcken an alle Prozesse Verteilen von unterschiedlich großen Blöcken an alle Prozesse

Gleichzeitiger/Nebenläufiger Zugriff auf den Senden-Puffer Folgender Code ist erst ab MPI 2.2 erlaubt: int sendbuf; MPI_Request req[2]; MPI_Isend(&sendbuf, 1, MPI_INT, 1, 1, MPI_COMM_WORLD, &req[0]); MPI_Isend(&sendbuf, 1, MPI_INT, 2, 1, MPI_COMM_WORLD, &req[1]); MPI_Waitall(2, &req);

70

Kapitel 10 ‒ Programmieren mit MPI

Ausblick: MPI-3 Erweiterung des Standards um folgende Funktionalität: Entfernter Speicherzugriff Ausfallsicherheit Weitere Varianten kollektiver Operationen

71

Kapitel 10 ‒ Programmieren mit MPI

Werkzeuge: Eine Auswahl Eclipse-Projekte Parallel Tools Platform (PTP) Setzt auf Eclipse CDT auf Enthält parallelen Debugger Analysewerkzeuge für MPI, OpenMP, UPC http://www.eclipse.org/ptp

gEclipse Bietet unter anderem: Werkzeuge für Fehlerfindung in Grid-Anwendungen Funktionen zur Visualisierung von Berechnungsergebnissen

http://www.geclipse.org/

72

Kapitel 10 ‒ Programmieren mit MPI

Werkzeuge: Marmot – MPI Correctness Checker Bibliothek für C und Fortran-Anwendungen, welche MPI1.2 verwenden Marmot erkennt Falsche Verwendung von MPI Nicht-portable Befehlskonstrukte Eventuelle Verklemmungen (durch Timeouts) und Wettlaufsituationen

Bettina Krammer et al. "MPI Correctness Checking with Marmot". In Proc. of 2nd HLRS Parallel Tools Workshop. Stuttgart, July 7-8 2008. 73

Kapitel 10 ‒ Programmieren mit MPI

Werkzeuge: SCALASCA Werkzeug zur inkrementellen Geschwindigkeitsanalyse auf großen Rechnersystemen. Unterstützung für MPI und OpenMP. Zu Analysierende Anwendung muss zuvor instrumentiert werden Benutzer kann Detailgrad der Analyse selbst festlegen, bspw. Geschwindigkeitsanalyse für bestimmte Pfade in der Anwendung.

M. Geimer, F. Wolf, B. J. N. Wylie, E. Abraham, D. Becker, B. Mohr: The SCALASCA Performance Toolset Architecture, Proc. Int'l Workshop on Scalable Tools for High-End Computing (STHEC, Kos, Greece), June 2008. 74

Kapitel 10 ‒ Programmieren mit MPI

Weitere Informationen Message Passing Interface Forum http://www.mpi-forum.org/ Argonne National Lab (University of Chicago) http://www-unix.mcs.anl.gov/mpi/ Open MPI: http://www.open-mpi.org/ Werkzeuge: Resch et al., Tools for High Performance Computing, Springer, 2008 Code-Beispiele: MPI-1: http://www-unix.mcs.anl.gov/mpi/usingmpi/examples/main.htm MPI-2: http://www-unix.mcs.anl.gov/mpi/usingmpi2/examples/main.htm

75

Kapitel 10 ‒ Programmieren mit MPI

Weitere Beispiele Beispiel-Implementierungen zur Berechnung von Mandelbrot- und Juliamengen: http://www.webacre.com/index.jsp?content=/cs/mandelbrot&footer=blank&splash=blank https://svn.mcs.anl.gov/repos/mpi/mpich2/branches/release/mpich2-1.2.1/examples

Linux-Live-CDs für Cluster: Cluster by Night: http://www.dirigibleflightcraft.com/research/CbN/ PelicanHPC: http://pareto.uab.es/mcreel/ParallelKnoppix/ uvm…

76

Kapitel 10 ‒ Programmieren mit MPI