32
1 Prof. Dr. H. Weber FB DCSM / Allg. Informatik Fachhochschule Wiesbaden Vertiefungsveranstaltung Systemprogrammierung SS 2009 Projekt Thema: Parallele Programmierung mit MPI - Message Passing Interface - Eine Ausarbeitung von: Nermin Sariyuez (Mat.-Nr.: 543469) und Anika Lawton (Mat.-Nr.: 749564)

MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

  • Upload
    others

  • View
    11

  • Download
    0

Embed Size (px)

Citation preview

Page 1: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

1

Prof. Dr. H. Weber FB DCSM / Allg. Informatik Fachhochschule Wiesbaden

Vertiefungsveranstaltung

Systemprogrammierung SS 2009

Projekt Thema:

Parallele Programmierung mit

MPI

- Message Passing Interface -

Eine Ausarbeitung von:

Nermin Sariyuez (Mat.-Nr.: 543469)

und

Anika Lawton (Mat.-Nr.: 749564)

Page 2: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

2

Inhaltsverzeichnis

1. Parallelisierung…………………………………………………………………….. 03

2. Einführung in MPI………………………………………………………………… 03

3. Basiskonzept von MPI…………………………………………………………….. 04

4. MPI Standards…………………………………………………………………….. 05

5. MPI Basis-Funktionen…………………………………………………………….. 05

6. MPI Programme…………………………………………………………………… 07

7. Rechnermodell……………………………………………………………………... 07

8. Erläuterungen zu einigen wichtigen Begriffen…………………………………….. 08

9. Datentypen in MPI………………………………………………………………… 09

9.1 Basisdatentypen……………………………………………………………. 09

9.2 Abgeleitete Datentypen…………………………………………………….. 10

10. Grundlagen der Kommunikation…………………………………………………... 11

11. Phasen der Kommunikation………………………………………………………... 11

12. Kommunikationsarten……………………………………………………………… 12

12.1 Punkt – zu – Punkt – Kommunikation……………………………………... 12

12.2 Kollektive – Kommunikation……………………………………………… 20

13. Verschiedene Buffer–Typen in MPI………………………………………………. 23

14. Kommunikationsmodi……………………………………………………………... 24

15. MPI–2……………………………………………………………………………… 25

16. MPI–Implementierungen für C/C++………………………………………………. 25

17. Kompilieren und Binden von MPI – Programmen………………………………… 25

18. Ausführen von parallelen Programmen (mpirun)………………………………….. 26

19. MPI-Programmbeispiel…………………………………………………………….. 28

20. MPI Funktionsübersicht……………………………………………………………. 30

Page 3: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

3

1. Parallelisierung Weil sich die Rechenleistung einzelner Prozessoren nicht unbegrenzt steigern lässt, ist es bei aufwendigen Rechnungen naheliegend, viele Prozessoren zusammenwirken zu lassen. Der simultane Zugriff vieler Prozessoren auf einen gemeinsamen Hauptspeicher stellt aber ein großes technisches Problem dar (Crossbar), deshalb dezentralisiert man auch den Speicher und kommt zu einem Cluster von Knoten, die jeweils über CPU(s), eigenes RAM und eine lokale Kopie des Betriebssystems verfügen und über ein (schnelles) Netz Daten austauschen können (message passing). Es bleibt dem Programmierer überlassen, dieses Zusammenspiel zu organisieren. Diese Aufgabe ist lösbar, denn ein geeignetes Kommunikationsmodell ist unter dem Namen “Message Passing Interface” (MPI) standardisiert und in mehreren freien Implementierungen (MPICH1, MPICH2, OpenMPI) verfügbar. Damit stehen die in MPI definierten Kommunikationsfunktionen als Bibliotheksaufrufe bereit.

2. Einführung in MPI ? MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch expliziten Austausch von Nachrichten. Der Message Passing Interface Standard spezifiziert eine Programm-Bibliothek von Funktionen für die Kommunikation in parallelen Systemen auf der Grundlage von Message Passing. Er legt dabei eine Sammlung von Operationen und ihre Semantik, also wie bereits erwähnt, eine Programmierschnittstelle fest, aber kein konkretes Protokoll und keine Implementierung für die Synchronisation und den Datenaustausch zwischen den einzelnen parallelen Prozessen (Multiprozessorsystemen). MPI-Schnittstellen werden für Programme in C, C++ und Fortran definiert. Die Kommunikationsanweisungen werden in C-Programmen als Funktionen bzw. in Fortran-Programmen als Subroutinen aufgerufen. Wir beschränken uns im Folgenden auf die C-Schnittstellen. Eine MPI-Applikation besteht in der Regel aus mehreren miteinander kommunizierenden Prozessen, die alle zu Beginn der Programmausführung parallel gestartet werden. Somit läuft dasselbe Programm gleichzeitig auf N Prozessoren jeweils als selbstständiger Prozess. All diese Prozesse arbeiten dann gemeinsam an einem Problem und nutzen zum Datenaustausch Nachrichten, welche explizit von einem zum anderen Prozess geschickt werden. Ein Vorteil dieses Prinzips ist es, dass der Nachrichtenaustausch auch über Rechnergrenzen hinweg funktioniert. Parallele MPI-Programme sind somit sowohl auf PC-Clustern (hier funktioniert der Austausch der Nachrichten z. B. über TCP), als auch auf dedizierten Parallelrechnern (hier läuft der Nachrichtenaustausch dann z. B. über den gemeinsamen Hauptspeicher).

Page 4: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

4

Programmiermodell: Austausch von Nachrichten (Messages)

3. Basiskonzepte von MPI MPI wurde 1993-94 von einer Gruppe aus Wissenschaft und Industrie entwickelt. Ziel der Entwickler, des MPI-Forums (MPIF), war es, einen Standard für portierbare Programme auf der Basis des MIMD-Modells (multiple instruction multiple data) zu schaffen. MPI setzt voraus, dass die Anzahl der Prozesse von Anfang an feststeht. Algorithmen, die dynamisch Tasks erzeugen, müssen entweder umgeschrieben werden, so dass sie diese Voraussetzung erfüllen, oder man muss doch auf eine Alternative zu MPI ausweichen. MIMD bedeutet, jeder Prozess arbeitet mit eigenen Daten und einem eigenen Programm. Die Prozesse kommunizieren miteinander via Message Passing, d.h. der benötigte Datenaustausch geschieht, indem Nachrichten von einem Prozess zum Nächsten geschickt werden. Im Quellcode der Programme (normaler Code in einer sequentiellen Programmiersprache - hier C) werden dafür Routinen aus der MPI-Bibliothek gerufen, die den gewünschten Kommunikationsvorgang realisieren. MPI ist eine recht umfangreiche Bibliothek, die außer synchroner und asynchroner Punkt-zu-Punkt-Kommunikation noch weitere Konzepte zur Verfügung stellt. Ziel des Standardisierungsprozesses war die Bereitstellung einer parallelen Programmierumgebung, die unabhängig von Hardware und Betriebssystemen ist. So können MPI-Programme sowohl auf Parallelrechnern als auch auf Workstation-Clustern abgearbeitet werden, ohne den Quellcode diesbezüglich ändern zu müssen.

Data Data Data

Sub- program

Sub- program

Sub- program

Kommunikations- Netzwerk

· · · · · · · ·

Page 5: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

5

4. MPI – Standards Es gibt zwei gültige Standards für MPI-Implementierungen: MPI-1 : ist der erste MPI-Standard, der auch heute für viele Implementierungen eingesetzt wird. MPI-1 Routinen basieren auf zweiseitiger Kommunikation. MPI-2 : hier sind als Funktionalität einseitige Kommunikation, paralleles I/O und Weiteres hinzugefügt. In MPI-2 sind im Gegensatz zu MPI-1 das Prozessmanagement und der Start von parallelen Programmen (im Falle von MPI-2 über mpiexec) getrennt ausgeführt.

5. MPI – Basis – Funktionen MPI besitzt 125 Funktionen und davon sind 6 die wichtigsten die hier in typischer Reihenfolge vorgestellt werden:

1. MPI_Init (&argc, &argv);

MPI_Init() ist die Initialisierungsroutine von MPI. Sie muss vor dem Benutzen von MPI-Befehlen ausgeführt, dh. vor allen anderen MPI-Funktionen aufgerufen werden. Die Argumente lauten argc und argv und sind Pointer auf Parameter der Hauptfunktion.

2. MPI_Comm_size (MPI_Comm comm, int size);

MPI_Comm_size() gibt die Anzahl der beteiligten Prozesse durch das zweite Argument „size“ zurück. Das erste Argument ist ein Kommunikator. Letztenendes ist das eine Anzahl von Prozessen, die Nachrichten austauschen. Für einfache Programme wird jedoch nur MPI_Comm_World benötigt. Dies beinhaltet alle Prozesse, die laufen, wenn die Ausführung beginnt.

3. MPI_Comm_rank (MPI_Comm comm, int rank);

MPI_Comm_rank() gibt den Rang, dh. den Prozessidentifikation zurück. 4. MPI_Send (start, count, datatype, dest, tag, comm);

MPI_Send() ist die Standardfunktion zum Senden von Nachrichten: „start“ bestimmt die Speicheradresse, „count“ die Länge und Datatype den Datentyp (hier immer MPI-Datentypen, keine C- oder Fortran-Datentypen). Tags sind zur Bestimmung von Kontexten, z.B. wenn zwei Nachrichten zwei floats senden, die einmal verarbeitet und einmal gedruckt werden sollen. Dest (Send) und Source (Receive) sind die Ränge der sendenden oder empfangenden Prozesse. MPY_ANY_SOURCE ist eine Wildcard für source, wenn man von irgendeinem Prozess statt eines speziellen empfangen will.

Page 6: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

6

5. MPI_Recv (start, count, datatype, source, tag, comm, status);

MPI_Recv() ist die Standardfunktion zum Empfang von Nachrichten. 6. MPI_Finalize ();

sauberes Beenden von MPI, dh. beendet MPI und räumt sämtlichen hinterlassenen Müll von MPI weg.

Diese 6 Basisfunktionen ermöglichen bereits sehr komplexe Programme. Im Prinzip wird die gesamte Kommunikation über nur zwei Befehle bewerkstelligt, und zwar durch MPI_Send() –verschickt eine Botschaft an einen anderen Prozess– und MPI_Recv() –versucht eine Botschaft zu empfangen–. Ein kurzes „Hello World“ MPI-Programm mit Prozess-Informationen könnte in etwa so aussehen: Kompilierung und Ausgabe:

# include <stdio.h> # include "mpi.h" int main(int argc, char** argv){

int myrank; /* Prozessrang */ int numprocs; /* Anzahl der Prozesse */

/* MPI initialisieren */ MPI_Init(&argc, &argv); /* Anzahl der Prozesse ermitteln */ MPI_Comm_size(MPI_COMM_WORLD, &numprocs); /* Prozessrang ermitteln */ MPI_Comm_rank(MPI_COMM_WORLD, &myrank);

printf("Hello world!\n" "I am process %d out of %d p’s.\n",myrank, numprocs); /* MPI beenden */ MPI_Finalize(); return 0; }

anika@anika:~/Desktop$ mpicc hello.c –o hello anika@anika:~/Desktop$ mpirun -np 4 hello Hello world! I am process 0 out of 4 p’s. Hello world! I am process 2 out of 4 p’s. Hello world! I am process 1 out of 4 p’s. Hello world! I am process 3 out of 4 p’s.

Page 7: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

7

6. MPI – Programme Voraussetzung im Source-Code für ein MPI-Programm ist, dass zuerst die Bibliothek mittels #include <mpi.h> eingebunden werden muss. Alle Bibliotheksfunktionen, Datentypen, Konstanten beginnen mit MPI_ und sind in mpi.h deklariert und geben bei Erfolg MPI_SUCCESS zurück, dh. alle MPI-Funktionen haben int als Return-Typ, der sich mit der Konstanten MPI_SUCCESS vergleichen lässt. Die Kommandozeilenparameter des Programms müssen an MPI_Init übergeben werden (die Parameter werden automatisch von dem Run-Script mpirun zugewiesen). MPI_Init muss vor allen anderen MPI-Funktionen aufgerufen werden. Nach der letzten MPI-Funktion muss der Befehl MPI_Finalize aufgerufen werden, um der MPI-Umgebung das Programmende mitzuteilen. Nach dem Befehl MPI_Finalize darf keine andere MPI-Funktion mehr aufgerufen werden. Ein MPI-Programm besteht in der Regel aus mehreren Prozessen. Prozesse werden zu Gruppen zusammengefasst, so genannten Kommunikatoren (engl. communicator). Initiale Gruppe ist der Standard-Kommunikator MPI_COMM_WORLD. Ein Prozess (meist der erste gestartete) bekommt in dieser Gruppe von der MPI-Laufzeitumgebung den Rang 0 zugewiesen. Die Ränge aller weiteren Prozesse werden laufend durchnumeriert. Es ist jederzeit möglich, neue Kommunikatoren zu erstellen und verschiedene Topologien auf diese Kommunikatoren zu mappen, dh. Prozesse können mehreren Kommunikatoren angehören, besitzen in den verschiedenen Kommunikatoren i. a. verschiedene Ränge. Neue Kommunikatoren erzeugt man z. B. mit MPI_Comm_split(). Jeder Prozess kann mittels MPI-Funktionen feststellen, wie viele Prozesse es insgesamt gibt (MPI_Comm_size) und seine eindeutige Nummer ermitteln (MPI_Comm_rank).

7. Rechnermodell Aus der Sicht des MPI-Programmierers stellen sich alle Parallelrechner gleich dar, unabhängig von ihrer tatsächlichen Architektur. Das einheitliche Rechnermodell hat den Vorteil, dass in MPI geschriebene Programme ohne Aufwand auf ein anderes System oder eine andere Architektur portiert werden können.

Jeder MPI-Prozess auf dem Rechner hat seinen eigenen Speicher. Die Kommunikation findet über den Austausch von Nachrichten (message passing) statt. Dabei kann jeder Prozess direkt mit jedem anderen kommunizieren, unabhängig von der Netztopologie. Die Anzahl der Prozesse eines parallelen Programms wird bei MPI mit size bezeichnet. Jeder Prozess erhält eine ID, den rank.

Page 8: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

8

8. Erläuterungen zu einigen wichtigen Begriffen Interkommunikator Kommunikator, über den mehrere Prozessgruppen kommunizieren. Intrakommunikator Kommunikator, über den mehrere Prozesse miteinander kommunizieren. Kommunikator Einem Kommunikator liegt eine Gruppe von Prozessen zugrunde, er ist jedoch mehr als eine Prozessgruppe. Der Kommunikator stellt einen Kontext für die Kommunikation zur Verfügung, dh. er besitzt einen eigenen, sicheren tag-Raum, so dass Nachrichten, die im Zusammenhang mit verschiedenen Berechnungen in verschiedenen Kommunikatoren ausgetauscht werden, sich trotz gleicher tags nicht gegenseitig stören können. Sie stören sich auch dann nicht, wenn die Prozessgruppen, die beiden Kommunikatoren zugrunde liegen, identisch sind. Es ist keine Kommunikation außerhalb eines Kommunikators möglich. Ein Kommunikator kann ein Attribut besitzen. Vordefinierte Kommunikatoren sind: - MPI_COMM_WORLD -enthält alle Prozesse - MPI_COMM_SELF -enthält nur den jeweiligen Prozess selbst - MPI_COMM_NULL -enthält keinen Prozess Aus den ersten beiden und ihren Gruppen kann man beliebige neue Kommunikatoren ableiten. Rang Der Rang wird von MPI zum Identifizieren eines Prozesses verwendet. Die Rangnummer ist innerhalb eines Kommunikators eindeutig. Dabei wird stets von Null beginnend durchnumeriert. Sender und Empfänger bei Sendeoperationen oder die Wurzel bei kollektiven Operationen werden immer mittels Rang angegeben. Message / Nachricht Punkt-zu-Punkt-Kommunikation in MPI funktioniert über Nachrichten. Eine Nachricht besteht zum einen aus den zu übermittelnden Daten nebst Angabe von Anzahl und Datentyp und zum anderen aus einer Art Briefumschlag, auf dem der Empfänger, ein tag, der Kommunikator und der Sender (implizit) angegeben sind. Prozessgruppe Menge von Prozessen, zusammengefasst werden, um gemeinsam eine Aufgabe zu erledigen. Damit sie miteinander kommunizieren können, benötigen sie einen Kommunikator. Ein solcher lässt sich zu jeder Gruppe konstruieren. tag Eine Art Stempel zur Identifikation einer Nachricht. Das tag ist ein Integerwert zwischen 0 und MPI_TAG_UB, der vom Programmierer vergeben wird. Topologie Mit Topologie oder virtueller Topologie ist die logische Anordnung des Prozesses in einer Gruppe gemeint. Dabei bleibt die hardwaremäßige Anordnung der Prozessoren dem Nutzer verborgen. MPI nimmt selbst eine Abbildung der logischen Struktur auf die physikalische

Page 9: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

9

vor. Unterschieden werden allgemeine Graphtopologien und als Spezialfall kartesische Topologien.

9. Datentypen in MPI Die MPI-Typen sind keine Datentypen im Sinne der Programmiersprachen, es können keine expliziten Instanzen dieser Typen erzeugt werden. Die MPI-Datentypen definieren ein Speicherlayout. 9.1 Basisdatentypen Die Basisdatentypen stehen nach MPI_Init immer zur Verfügung und müssen im Gegensatz zur abgeleiteten Datentypen nicht erst angemeldet werden. Sämtliche abgeleiteten Datentypen werden aus ihnen konstruiert. Im Folgenden werden die vordefinierten Datentypen von MPI und die korrespondierenden Datentypen in C wiedergegeben:

MPI Datentypen C-Datentypen MPI_CHAR MPI_SHORT MPI_INT MPI_LONG MPI_UNSIGNED_CHAR MPI_UNSIGNED_SHORT MPI_UNSIGNED MPI_UNSIGNED_LONG MPI_FLOAT MPI_DOUBLE MPI_LONG_DOUBLE MPI_PACKED MPI_BYTE

signed char signed short int signed int signed long int unsigned char unsigned short int unsigned int unsigned long int float double long double

Zu den Datentypen MPI_PACKED und MPI_BYTE gibt es keine korrespondierenden Datentypen in C. Ein Wert vom Type MPI_BYTE besteht aus einem Byte, das nicht mit einem Character identisch sein muss. Es kann benutzt werden, wenn man keine Umwandlung zwischen verschiedenen Daten-Repräsentationen wünscht. Weiterhin kann man Arrays {MPI_TYPE_vector (count, blocklength, stride, oldtype,

&newtype)} und Verbunde {MPI_Type_structure (count, array of length, array of location,

array of types, &newtype)} definieren. Hierbei steht stride für den Abstand der Elemente. Man kann Daten aber auch noch anders gruppieren. Mittels MPI_Pack und MPI_Unpack kann man nicht zusammenhängende Speicherregionen zusammenpacken und umgekehrt. Hierfür wird der Datentyp MPI_PACKED benötigt, welches für spezielle Packprozeduren verwendet wird. MPI-Datentypen ermöglichen:

Page 10: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

10

• Maschinenunabhängigkeit (z. B. C-Typ char repräsentiert nicht auf allen Maschinen ein Byte)

• Angabe der Nachrichtenlänge in Elementen vom jeweiligen Typ

9.2 Abgeleitete Datentypen Ein MPI-Programm kann zur Laufzeit neue, eigene Datentypen definieren. Diese werden zusammengesetzt aus den Basisdatentypen bzw. aus vorher definierten eigenen Datentypen. Durch die Definition eigener Datentypen, werden Programme einfacher und übersichtlicher. Über selbst definierte Typen lassen sich insbesondere auch nicht

zusammenhängende Daten beschreiben. Unabhängig von der verwendeten Routine erfordert der Aufbau eines neuen Datentypes in MPI immer zwei Schritte :

• Definition bzw. Beschreibung des neuen Datentyps als Vektor- oder Strukturdatentyp

• Registrierung, bzw. Bekanntgabe des neuen Datentypes, um ihn für Kommunikation nutzen zu können

MPI stellt Konstruktoren zur Verfügung, mit denen eigene (abgeleitete) Datentypen definiert werden können:

• MPI_Type_contiguous

gleichartige Elemente in einem zusammenhängenden Speicherbereich.

• MPI_Type_vector

Blöcke aus Elementen des gleichen Basistyps mit Displacement für die Blockanfänge.

• MPI_Type_hvector

Blöcke aus Elementen des gleichen Basistyps mit Displacement (in Byte) für die Blockanfänge.

• MPI_Type_indexed

unterschiedlich große Blöcke aus Elementen des gleichen Basistyps mit unterschiedlichen Displacements.

• MPI_Type_hindexed

unterschiedlich große Blöcke aus Elementen des gleichen Basistyps mit unterschiedlichen Displacements (in Byte).

• MPI_Type_struct

erlaubt verschiedene Größen und Displacements der Blöcke, sowie die Basistypen der einzelnen Blöcke dürfen unterschiedlich sein.

Abgeleitete Typen können ihrerseits wiederum als Basistypen für neue Typen verwendet werden. Durch die Verwendung nutzerdefinierter Datentypen wird die effiziente Übertragung komplizierterer Datenstrukturen wesentlich erleichtert. Die neuen Datentypen können rekursiv aus bereits definierten Datentypen generiert werden und sind dem Nutzer über Type-Handles zugänglich. Vor der Benutzung müssen abgeleitete Datentypen mittels MPI_Type_commit angemeldet und später mit MPI_Type_free abgemeldet werden

Page 11: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

11

10. Grundlagen der Kommunikation Messages bilden die Grundlage der Kommunikation zwischen den einzelnen Knoten, dh. das Senden und Empfangen von Nachrichten ist der grundlegende Mechanismus für die Kommunikation in MPI. Eine Message besteht grundsätzlich aus zwei Teilen, aus Umschlag und Nachricht. Ähnlich einem Briefumschlag besitzt jede Nachricht eine Umschlag, welcher

• Sender

• Empfänger

• Tag

• Kommunikator

angibt. Sender und Empfänger werden durch ihren Rang (rank) im Kommunikator identifiziert. Der Rang ist innerhalb eines Kommunikators eindeutig und identifiziert einen Prozess. Der Kommunikator bezeichnet den Kontext, in welchem die Kommunikation stattfindet. Außerdem ist mit dem Kommunikator eine Gruppe von Prozessen festgelegt. Innerhalb des Kommunikators können Nachrichten nur an die Prozesse gesendet werden, die der Prozessgruppe angehören. Ein vordefinierter Kommunikator, welcher die Kommunikation zwischen allen Prozessen gestattet, ist MPI_COMM_WORLD. Wenn die Problemstellung keine Arbeit mit speziellen Kontexten erfordert, kann man immer, wenn ein comm-Argument verlangt wird, MPI_COMM_WORLD verwenden. Das message tag (tag) ist ein Integerwert und dient als eine Art Stempel, dh. Es dient der Klassifizierung der Nachricht. Es erlaubt die Unterscheidung von Nachrichten, und wird bei der Empfangsoperation abgefragt. Das tag kann Werte von 0 bis UB annehmen, wobei der höchste Wert für Tags in MPI_TAG_UB definiert ist. Die Nachricht selbst besteht aus 3 Teilen, aus der Nachricht selbst, dem Datentyp und der Anzahl der gesendeten Elemente.

11. Phasen der Kommunikation Eine Nachrichtentransfer/ Kommunikation wird intern in drei Schritten realisiert:

1. Die Daten werden aus dem Sendepuffer „smessage“ in einen Systempuffer kopiert und eine Nachricht erzeugt, indem ein Header hinzugefügt wird.

Diese Header enthält dann Informationen über den Sender der Nachricht, den Zielprozess, die Markierung und den Kommunikator.

2. Nachricht wird vom Sender zum Empfänger übertragen.

3. Die Daten von der eingegangenen Nachricht werden vom Empfänger „dest“ aus dem Systempuffer in den Empfangspuffer geschrieben.

Page 12: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

12

12. Kommunikationsarten MPI kennt verschiedene Arten der Kommunikation zwischen den Prozessen. Die Kommunikation zwischen Prozessen eines parallelen Programms ist ein wichtiger Faktor für dessen Schnelligkeit. Daher gibt es eine Vielzahl unterschiedlicher Funktionen, die im Folgenden vorgestellt werden. Es existieren zwei Gruppen von Funktionen für die Kommunikation. Die erste beinhaltet die Methoden für die Punkt-zu-Punkt-Kommunikation, also die Kommunikation zwischen zwei Prozessen. Die zweite Gruppe beinhaltet die Methoden für die kollektive Kommunikation, wobei mehrere Prozesse miteinander kommunizieren. Die Punkt-zu-Punkt-Kommunikation teilt sich in weitere zwei Gruppen, die blockierende- und die nicht blockierende-

Kommunikation. 12.1 Punkt – zu – Punkt – Kommunikation An der Punkt-zu-Punkt-Kommunikation sind immer zwei Prozesse eines Kontextes beteiligt und die Kommunikation findet innerhalb eines Kommunikators statt. Abb.: Punkt-zu-Punkt-Kommunikation innerhalb eines Kommunikators

Die Operationen sind in der Regel asynchron, es ist aber von der Implementierung und der Nachrichtengröße abhängig.

Prozess 1 Prozess 4

Prozess 2

Prozess 3

Prozess N Prozess 5 source

destination

message

communicator

Page 13: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

13

Blockierend bedeutet: Sendefunktion wird erst beendet, wenn die Nachrichteninitiierung so weit fortgeschritten ist, dass der (Anwendungs-) Sendepuffer nicht durch eine nachfolgende Anweisung überschrieben werden kann, d.h. erst nach dem das Lesen aus dem Sendepuffer bzw. Schreiben in den Empfangspuffer Vollendet ist, kehrt der Sende- bzw. Empfangsaufruf mit freien Ressourcen zurück. Nicht-Blockierend bedeutet: Der Benutzer ist selbst dafür verantwortlich, dass die abgehende Nachricht nicht korrumpiert wird. Das MPI System wird nur mit der Nachrichtensendung beauftragt.

Aufruf kehrt vor Vollendung zurück, Kommunikation läuft parallel zum weiteren Programm ab, Kontrollfunktionen stehen zur Verfügung.

Page 14: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

14

Die grundlegenden Operationen für eine Punkt-zu-Punkt-Kommunikation sind senden und empfangen: MPI_Send (Standard-Send für blockierende Kommunikation): Senden einer Nachricht an einen anderen Prozess innerhalb eines Kommunikators. Syntax:

• MPI_Send (buf, count, datatype, dest, tag, comm) Input Parameter:

• buf: Anfangsadresse des Sendepuffers • count: Anzahl der Elemente des Sendepuffers (integer, nichtnegativ) • datatype: Typ der Elemente im Sendepuffers (handle) • dest: Rang des Empfängerprozesses in comm (integer) • tag: Markierung der Nachricht, zur Unterscheidung verschiedener

Nachrichten (integer); Ein Kommunikationsvorgang wird durch ein Tripel (Sender, Empfänger, tag) eindeutig beschrieben.

• comm: Kommunikator der Prozessgruppe (handle) „count“ Datenobjekte vom Typ „datatype“ ab Adresse „buf“ (-> Sendepuffer) werden

mit der Kennung „tag“ an den Prozess mit Rang „dest“ innerhalb des

Kommunikators „comm“ gesendet.

Rückgabewert:

• Integerwert (int) WICHTIG:

Diese Routine kann den Sender solange blockieren, bis die Nachricht empfangen worden ist.

MPI_Recv (Standard-Receive für blockierende Kommunikation): Empfangen einer Nachricht. Syntax:

• MPI_Recv (buf, count, datatype, source, tag, comm, status) Output Parameter:

• buf: Anfangsadresse des Empfangspuffers ausreichender Größe • status: Zeiger auf eine Statusstruktur, in der Informationen über die

empfangene Nachricht abgelegt werden sollen, d.h. es wird source und tag angegeben (MPI_Status) Input Parameter:

• count: Anzahl der Elemente im Empfangspuffer (integer, nichtnegativ) • datatype: Typ der zu empfangenden Elemente (handle) • source: Rang des Senderprozesses in comm oder MPI_ANY_SOURCE

(mit source=MPI_ANY_SOURCE wird von einem beliebigen Prozess empfangen) (integer)

Page 15: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

15

• tag: erwartete Markierung der Nachricht zur Unterscheidung verschiedener Nachrichten. Ein Kommunikationsvorgang wird durch ein Tripel (Sender, Empfänger, tag) eindeutig beschrieben.

Um Nachrichten mit beliebigen tags zu empfangen, benutzt man die Konstante MPI_ANY_TAG, d.h. jede Nachricht wird empfangen.

• comm: Kommunikator der Prozessgruppe (handle) Rückgabewert:

• Integerwert (int) ANMERKUNG:

Das Argument „count“ gibt die maximale Länge einer Nachricht an. Wieviele Elemente tatsächlich empfangen wurden, lässt sich mit MPI_Get_count ermitteln.

Die beiden Operationen sind blockierend und asynchron. Das bedeutet: • MPI_Recv kann ausgeführt werden, bevor das zugehörige MPI_Send gestartet wurde. • MPI_Recv blockiert, bis die Nachricht vollständig empfangen wurde.

Analog gilt: • MPI_Send kann ausgeführt werden, bevor das zugehörige MPI_Recv gestartet wurde. • MPI_Send blockiert, bis der Sendepuffer wieder verwendet werden kann (d.h. die

Nachricht vollständig übermittelt oder zwischengepuffert wurde).

Problem: MPI_Send und MPI_Recv arbeiten asynchron und blockierend. Deadlock: Beim Nachrichtenaustausch können bei der blockierenden Kommunikation so genannte Deadlocks entstehen. Zum Beispiel wenn zwei Prozesse durch Recv() auf eine Nachricht vom jeweils anderen warten. Deshalb muss die Abfolge des Sendens und Empfangens beachtet werden. Soll zwischen Prozess 0 und Prozess 1 jeweils eine Nachricht versendet und empfangen werden, so kann der spezielle Befehl SendRecv() (-> Gleichzeitiges Senden und Empfangen ohne Gefahr eines Deadlocks) verwendet werden, was die Gefahr eines Deadlocks im Programm verringert. Wenn der Zwischenpuffer zu klein ist oder kein Zwischenpuffer verwendet wird, kommt es zum Deadlock!

Page 16: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

16

Beispiel für blockierende Kommunikation mit MPI_Send und MPI_Recv für 2 MPI Prozesse:

Die Effizienz einer parallelen Anwendung kann oftmals gesteigert werden, indem man Kommunikation mit Berechnung überlappt. Dazu definiert der MPI Standard sogenannte nichtblockierende Kommunikation, bei der die Kommunikationsoperation lediglich angestoßen wird. Eine separate Funktion muss dann aufgerufen werden, um solch eine Operation zu beenden. Die nicht-blockierende Punkt-zu-Punkt-Kommunikation unterscheidet sich in Bezug auf die blockierende, dass nach dem Aufruf einer Funktion das Programm gleich fortgesetzt wird. Die Kommunikation also im Hintergrund stattfindet. Währendessen hat man "Zeit" für andere Dinge. Allerdings dürfen unter keinen Umständen Variablen, die an der Kommunikation beteiligt sind, geändert werden. Erst nach einer Überprüfung auf erfolgreiches Senden oder Empfangen dürfen diese wieder verändert werden. Bei der nicht-blockierende Kommunikation werden die Standard-Operationen MPI_Isend und MPI_Irecv verwendet. MPI_Isend (Standard-Send für nicht-blockierende Kommunikation): Startet eine nicht-blockierende Standard-Sendeoperation. Syntax:

• MPI_Isend(buf, count, datatype, dest, tag, comm, request)

#include "mpi.h" #include <stdio.h> #include <string.h> int main(int argc, char *argv[]){ char message[20]; int myrank, tag=99; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if (myrank == 0) { strcpy(message, "Hello, there"); MPI_Send(message, strlen(message)+1, MPI_CHAR, 1, tag, MPI_COMM_WORLD); } else { MPI_Recv(message, 20, MPI_CHAR, 0, tag, MPI_COMM_WORLD, &status); printf("received \"%s\"\n", message); } MPI_Finalize(); return 0; }

Page 17: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

17

Input Parameter:

• buf: Anfangsadresse des Sendepuffers • count: Anzahl der Elemente des Sendepuffers (integer, nichtnegativ) • datatype: Typ der Elemente des Sendepuffers (handle) • dest: Rang des Empfängerprozesses in comm (integer) • tag: message tag zur Unterscheidung verschiedener Nachrichten

(integer); Ein Kommunikationsvorgang wird durch ein Tripel (Sender, Empfänger, tag) eindeutig beschrieben.

• comm: Kommunikator (handle)

Output Parameter

• request: Request-Objekt; wird benötigt, um die Sendeoperation zu beenden (handle)

ANMERKUNG:

Das Ende der Sendeoperation kann mit MPI_Wait abgewartet oder mit MPI_Test

getestet werden. Erst nach dem erfolgreichen Beenden dürfen die Daten im Sendepuffer modifiziert werden. MPI_Irecv (Standard-Receive für nicht-blockierende Kommunikation): Startet eine nicht-blockierende Empfangsoperation Syntax:

• MPI_Irecv (buf, count, datatype, source, tag, comm, request) Input Parameter:

• count: Anzahl der Elemente im Empfangspuffer (integer) (muss größer oder gleich der Länge der Nachricht sein)

• datatype: Datentyp der Elemente des Empfangspuffers (handle) • source: Rang des Senders in comm oder MPI_ANY_SOURCE (integer) • tag: Markierung der Nachricht (integer)

Eine Nachricht wird durch Sender, Empfänger, tag und Kommunikator eindeutig zugeordnet. Dabei ist der Sender implizit gegeben.

• comm: Kommunikator (handle)

Output Parameter:

• buf: Anfangsadresse des Empfangspuffers • request: Request-Objekt (handle). Adresse der Datenstruktur, die Informationen zur Operation enthält.

ANMERKUNG:

Beim Aufruf von MPI_Irecv wird ein Request-Objekt allociert, welches solange aktiv bleibt, bis die Empfangsoperation beendet ist, d.h. bis die Daten in den Empfangspuffer kopiert sind oder die Operation abgebrochen wurde.

Page 18: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

18

Beispiel für nicht- blockierende Kommunikation mit MPI_Isend und MPI_Irecv:

Überprüfung des Status von nicht-blockierenden Send/Receive: Mittels der Funktionen MPI_Wait und MPI_Test kann man den Status einer nicht-

blockierende Kommunikation überprüfen. Jede nicht-blockierende Kommunikation muss/sollte mit den folgenden Completion-Check abgeschlossen werden, um eventuell belegte Systemresourcen wieder freizugeben (implementationsabhängig). MPI_Wait: Blockiert den rufenden Prozess solange, bis eine Sende- oder Empfangsoperation beendet ist. MPI_Wait kehrt zurück, sobald die Kommunikation vollständig abgearbeitet ist. MPI_Test: Prüft, ob eine Sende- oder Empfangsoperation beendet ist. MPI_Test ist die nichtblockierende Variante von MPI_Wait, der Kommunikations-Zustand wird als Flag zurückgeliefert. MPI_Cancel: Hiermit kann eine nichtblockierende Operation vor ihrer Beendigung abgebrochen werden.

#include <stdio.h> #include <mpi.h> int main (int argc, char *argv[]) { char buf[20]; int myrank, tag; MPI_Status status; MPI_Request request MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if (myrank == 0){ MPI_Isend(buf, 20, MPI_CHAR, 1, 815, MPI_COMM_WORLD, &request); } else{ MPI_Irecv(buf, 20, MPI_CHAR, 0, 815, MPI_COMM_WORLD, &request); } /* Berechnungen durchführen */ MPI_Wait(&request, &status); MPI_Finalize(); return 0; }

Page 19: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

19

Weitere Funktionen für: blockierende Kommunikation: Senden:

• MPI_Bsend (gepuffert) • MPI_Rsend (ready-Modus) • MPI_Send (Standard) • MPI_Ssend (synchron)

Empfangen:

• MPI_Recv Gleichzeitig senden und empfangen:

• MPI_Sendreceive • MPI_Sendreceive_replace

nicht-blockierende Kommunikation: Senden:

• MPI_Ibsend (gepuffert) • MPI_Irsend (ready-Modus) • MPI_Isend (Standard) • MPI_Issend (synchron)

Empfangen:

• MPI_Irecv Nicht-blockierende Operationen beenden: (sowohl für Senden als auch Empfangen)

• MPI_Test • MPI_Testall • MPI_Testany • MPI_Testsome • MPI_Wait • MPI_Waitall • MPI_Waitany

Aufbau einer stehenden Kommunikationsanforderung:

• MPI_Waitsome • MPI_Bsend_init (für gepufferte Kommunikation) • MPI_Rsend_init (für Kommunikation im "ready"-Modus) • MPI_Send_init (für Standard-Kommunikation) • MPI_Ssend_init (für synchrone Kommunikation) • MPI_Recv_init

Steuerung:

• MPI_Request_free (gibt ein Request-Objekt frei) Test auf Vorhandensein einer Nachricht: (ohne die Nachricht zu empfangen)

• MPI_IProbe • MPI_Probe

Page 20: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

20

12.2 Kollektive – Kommunikation In parallelen Anwendungen trifft man nicht selten spezielle Kommunikationsmuster an, bei denen mehrere MPI-Prozesse gleichzeitig beteiligt sind. Der Standard hat deswegen für einige übliche Operationen eigene Funktionen definiert. Bei vielen davon gibt es einen ausgewählten MPI-Prozess, der eine Sonderrolle einnimmt, und der typischerweise mit root bezeichnet wird. Broadcast (ausstrahlen) Mit der Broadcast-Operation schickt ein ausgewählter MPI-Prozess „root“ allen anderen Prozessen in seiner Gruppe „comm“ die gleichen Daten. Die dafür definierte Funktion ist dabei für alle beteiligten Prozesse identisch:

MPI_Bcast (buffer, count, type, root, comm)

Der MPI-Prozess root stellt in buffer seine Daten zur Verfügung, während die anderen Prozesse hier die Adresse ihres Empfangspuffers übergeben. Die restlichen Parameter müssen bei allen Prozessen gleich (bzw. gleichwertig) sein. Nachdem die Funktion zurückkehrt, befinden sich in allen Puffern die Daten, die ursprünglich nur bei root vorhanden waren. Gather (sammeln) Mit der Gather-Operation sammelt der MPI-Prozess root die Daten aller beteiligten Prozesse ein. Die Daten aller Sendepuffer werden dabei (nach Rang sortiert) hintereinander im Empfangspuffer abgelegt:

MPI_Gather (sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm) Scatter (streuen) Mit einer Scatter-Operation schickt der MPI Prozess root jedem beteiligten Prozess ein unterschiedliches aber gleichgroßes Datenelement:

MPI_Scatter (sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm)

Abb.: Die Broadcast-Operation für drei Prozesse

Abb.: Die Gather-Operation

Abb.: Die Scatter-Operation

Page 21: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

21

Akkumulation Die Akkumulation ist eine spezielle Form der Gather-Operation. Hierbei werden ebenfalls die Daten aller beteiligten Prozesse aufgesammelt, aber zusätzlich noch mittels einer festgelegten Reduktionsoperation zu einem Datum reduziert.

MPI_Reduce (sendbuf, recvbuf, count, type, op, root, comm)

Für den Parameter „op“ existieren dabei folgende vordefinierte Reduktionsoperationen: Logische Operationen

• MPI_LAND: logische UND-Verknüpfung • MPI_BAND: bitweise UND-Verknüpfung • MPI_LOR: logische ODER-Verknüpfung • MPI_BOR: bitweise ODER-Verknüpfung • MPI_LXOR: logische exklusiv-ODER-Verknüpfung • MPI_BXOR: bitweise exklusiv-ODER-Verknüpfung

Arithmetische Operationen • MPI_MAX: Maximum • MPI_MIN: Minimum • MPI_SUM: Summe • MPI_PROD: Produkt • MPI_MINLOC: Minimum mit Prozess • MPI_MAXLOC: Maximum mit Prozess

Die Operationen MPI_MINLOC und MPI_MAXLOC geben zusätzlich den Rang des MPI-Prozesses zurück, der das Ergebnis bestimmte. Benutzerdefinierte Operationen Zusätzlich zu den oben genannten Operationen können auch eigene definiert werden:

MPI_Op_create (function, commute, op) mit MPI_User_function *function Allgather Bei der Allgather-Operation schickt jeder Prozess an jeden anderen Prozess die gleichen Daten. Es handelt sich also um eine Multi-Broadcast-Operation, bei der es keinen gesonderten MPI-Prozess gibt.

MPI_Allgather (sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm)

Abb.: Die Allgather-Operation

Page 22: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

22

All-to-all (Gesamtaustausch) Bei der All-to-all-Kommunikation werden – ähnlich wie bei der Allgather-Kommunikation – Daten zwischen allen Prozessen ausgetauscht. Dabei wird jedoch nur der i-te Teil des Sendebuffers an den i-ten Prozess gesendet. Daten, die vom Prozess mit dem Rang j kommen, werden entsprechend an j-ter Stelle im Empfangsbuffer gespeichert.

MPI_Alltoall (sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm)

Des Weiteren gibt es noch die synchronisierende MPI_Barrier-Operation. Diese Funktion kehrt erst zurück, nachdem alle in der angegebenen Gruppe befindlichen MPI Prozesse diesen Teil des Programmes erreicht haben.

Abb.: Die All-to-all-Operation

Page 23: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

23

13. Verschiedene Buffer – Typen in MPI User buffer only:

Einfach für synchrones-Send, beim asynchrones-Send ohne Zwischenspeicher nicht möglich.

Usage of a system buffer S: Alternative für asynchrones-Send, hier wird die Nachricht zuerst in einen Zwischenspeicher kopiert. Probleme: Zusätzlicher Overhead, Nachricht zu groß für Zwischenspeicherung (Maximal verfügbarer Systemspeicher nicht zugänglich für den User).

Usage of a user-level buffer T: In diesem Falle sorgt der User für einen entsprechend großen Zwischenspeicher. Da jetzt der User weiß wie viel Speicher verfügbar ist, kann er die Nachricht gegebenenfalls in kleinere Stücke aufteilen.

Ganz anderer Ansatz: Verwendung von One-sided-Communication: Remote-Direct-Memory-Access (RDMA). Zu vergleichen mit DMA innerhalb eines Systems, nur auf einen entfernten

A

Prozess P

A M

B

Prozess Q

Abb.1: User buffer only

B

Abb.2: Usage of a system buffer S

Prozess P Prozess Q S

Prozess Q

B

A M

Prozess Q

T

Abb.3: Usage of a user-level buffer T

M

Page 24: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

24

Rechner. Der entfernte Rechner muss vor der Kommunikation ein Memory-Window freigeben, in dem bestimmte Rechner lesen/schreiben dürfen. Andere Kommunikationspartner können dann mit Put/Get-Operationen auf diesen Speicherbe-reich zugreifen.

14. Kommunikationsmodi MPI gibt dem Programmierer die Möglichkeit, das Kommunikationsprotokoll selbst zu bestimmen. Deshalb gibt es für die blockierende wie für die nichtblockierende Variante der Sendeoptionen vier Kommunikationsmodi bzw. Übertragungsmodi, die die Koordinaten der Sende- und Empfangsoperation bestimmen. Über den Modus entscheidet der Sender allein. Die verschiedenen Übertragungsmodi werden über verschiedene Kommunikations-anweisungen angesprochen. MPI-Kommunikationsmodis:

Standardmodus: Hier bleibt es MPI überlassen, ob die Nachrichten gepuffert werden oder ob aus Gründen der Performance darauf verzichtet wird. Es ist möglich, dass die Sendeoperation beendet ist, bevor eine passende Empfangsoperation gestartet wurde, muss jedoch nicht. Es darf in jedem Fall gesendet werden bevor die Empfangsoperation gestartet wurde.

Gepufferter Modus: Der Nutzer muss vor dem Senden selbst einen Puffer bereitstellen, in dem die Daten gespeichert werden, bis die Empfangsoperation sie benötigt. Die Sendeoperation wird beendet, unabhängig davon, ob eine passende Empfangsoperation gestartet wurde. Damit wird im Prinzip das Senden vom Empfangen unabhängig gemacht.

Synchroner Modus: Das Senden kann beginnen, egal ob eine Empfangsoperation gestartet wurde. Die Operation wird jedoch nicht beendet, bevor die passende Empfangsoperation begonnen hat, Daten in den Empfangspuffer zu kopieren. Wenn Sender und Empfänger blockierende Operationen verwenden, hat dieser Modus einen synchronisierenden Effekt.

Ready-Modus: Eine Sendeoperation darf nur gestartet werden, wenn bereits eine passende Empfangsoperation gestartet wurde. Auf einigen Rechnern erlaubt dies, auf das handshake-Verfahren, wie es beim synchronen Senden üblich ist, zu verzichten und die Performance zu verbessern. Die meisten MPI-Implementationen setzen derzeit den Ready-Modus mit dem Standardmodus gleich, so dass ein Performancegewinn mit dieser Variante nicht möglich ist.

Page 25: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

25

15. MPI – 2 Seit 1997 ist eine zweite Version des MPI-Standards verfügbar, die einige Erweiterungen zu dem weiterhin bestehenden MPI-1.1 Standard hinzufügt. Zu diesen Erweiterungen gehören unter anderem

• eine dynamische Prozessverwaltung, d.h. Prozesse können nun zur Laufzeit erzeugt und gelöscht werden

• [paralleler] Zugriff auf das Dateisystem

• einseitige Kommunikationsoperationen und Operationen zur parallelen Ein-/ Ausgabe

• Spezifikation zusätzlicher Sprachschnittstellen (wie C++) Da MPI-2-Standard eine Obermenge des MPI-Standards ist, ist jedes korrekte MPI-1-Programm auch ein korrektes MPI-2-Programm.

16. MPI – Implementierungen für C/C++ Die erste Implementierung des MPI-1.x-Standards war MPICH vom Argonne National

Laboratory und der Mississippi State University. Mittlerweile ist MPICH2 verfügbar, das den MPI-2.1-Standard implementiert. LAM/MPI vom Ohio Supercomputing Center war eine weitere freie Version. Ab der Version 1.35.0 der Boost Libraries gibt es Boost:MPI, eine C++ freundliche Schnittstelle zu verschiedenen MPI Implementierungen. Auch andere Projekte, wie z.B. TPO++, bieten diese Möglichkeit und sind in der Lage STL-Container zu versenden und zu empfangen.

17. Kompilieren und Binden von MPI – Programmen Es gibt spezielle Compilerskripte, um MPI Programme zu kompilieren und zu binden. Diese Skripte beginnen mit dem Präfix mpi: mpicc Kompilieren und Binden von C Programmen

mpicc.mpich Kompilieren und Binden von C Programmen im MPICH Kompatibilitätsmodus

Page 26: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

26

18. Ausführen von parallelen Programmen Parallele Programme können interaktiv oder unter der Kontrolle eines Batchsystems gestartet werden. Beim interaktiven Starten von Programmen ist es nicht möglich, einen anderen Knoten als den, auf dem man eingeloggt ist, zu benutzen. Die Syntax, um parallele Applikationen zu starten, ist

mpirun [ mpirun_Optionen ] program

oder

mpirun [ mpirun_Optionen ] -f appfile

sowohl für interaktive Aufrufe als auch für Aufrufe in Shellskripten, um Batchjobs auszuführen. Die mpirun_Optionen sind für beide Anwendungsarten gleich. Um eine parallele Anwendung als Batchjob zu starten, muss das Shellskript, das gewöhnlich für das Kommando job_submit erforderlich ist, das Kommando mpirun mit der Anwendung als Eingabedatei enthalten. Wichtig für das Verständnis: die Option -n # ist erforderlich, wenn mpirun interaktiv aufgerufen wird, wird aber beim Aufruf von mpirun in Batchjobs (die Anzahl der in Batchjobs benutzten Prozessoren wird durch eine Option des Kommandos job_submit kontrolliert) ignoriert. Es gibt keine Option, um die Anzahl der Knoten zu spezifizieren! Mpirun Optionen:

mpirun Option Kurze Erklärung

-n # or -np # MPI job läuft auf # Prozessoren (wird ignoriert in Batchjobs)

-m block | cycle

MPI Prozesse werden blockweise oder zyklisch auf die Prozessoren abgebildet; block: ordnet soviele Tasks wie möglich den CPUs eines Knotens zu, bevor eine Zuordnung zum nächsten Knoten erfolgt (default); cyclic: odnet Tasks ”round robin” über alle allokierten Knoten zu.

-cpu_bind [=block|=cyclic|...]

bindet einen Prozess an eine "locality domain" (ldom ist hier definiert als ein "socket", der 2 "cores" enthält - ein Rechenknoten hat 2 "sockets") und verhindert das Verschieben eines Prozesses nach dem Loslaufen auf eine andere ldom; Optionen sind: block: blockweise Zuweisung an jede ldom gemäß der "(packed) rank id" cyclic: zyklische Zuweisung an die ldoms eines Knoten gemäß der "(packed) rank id" (default) ...: viele andere Optionen sind erlaubt; Beschreibung mittels Eingabe von man mpirun auf XC2; die Option -cpu_bind (ohne weitere Parameter) verhindert das CPU-Binding.

Page 27: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

27

-mpich die Anwendung läuft im MPICH Kompatibilitätsmodus

-version druckt die Versionsnummer

-v schaltet wortreichen Modus an

-d schaltet Debug Modus an

-p schaltet "pretend mode" an

-ck schaltet erweiterten "pretend mode" an

-j druckt die HP MPI job ID

-T druckt "user and system time" für jeden MPI Prozess

-1sided ermöglicht einseitige Kommunikation

-nopreload

verhindert das vorzeitige Laden der gewöhnlichen MPI Bibliothek; muss benutzt werden, wenn die Anwendung mit einer anderen MPI Bibliothek wie mtmpi (multi-threaded) or pmpi (profiling) gebunden wird.

-i options ermöglicht die zeitliche Profilerstellung für alle Prozesse (genaueres im HP MPI User Guide)

-stdio=options

spezifiziert IO Optionen (genaueres im HP MPI User Guide); im Batchmodus werden alle Optionen außer -stdio=p ignoriert

Der letzte Parameter des Kommandos mpirun muss entweder die Option -f appfile oder ein ausführbares Programm bzw. ein Shellskript sein. Das Format der Datei appfile lautet folgendermaßen: in Zeile i muss der Name des ausführbaren Programms spezifiziert sein, das auf dem Prozessor i-1 laufen soll. Beispiel: Um ein "master-slave" Modell auf 4 Prozessoren laufen zu lassen, muss das folgende appfile erzeugt werden:

master slave slave slave

Der "master" läuft auf Prozessor 0 und die "slaves" auf den Prozessoren 1 bis 3.

Page 28: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

28

19. MPI – Programmbeispiel Beispiel für die Berechnung von Pi mit MPI:

#include "mpi.h" #include <stdio.h> #include <math.h> int main( int argc, char *argv[] ) { int n, myid, numprocs, i; double PIZahl= 3.141592653589793238462643; double mypi, pi, h, sum, x; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); while (1) { if (myid == 0) { printf("Anzahl der Intervalle: "); scanf("%d",&n); } MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); if (n == 0) break; else { // Der Viertelkreis wird in n Intervalle geteilt h = 1.0 / (double) n; sum = 0.0; // Solange i kleiner der Anzahl der Intervalle ist wird diese FOR-Schleife ausgeführt for (i = myid + 1; i <= n; i += numprocs) { x = h * ((double)i - 0.5); sum += (4.0 / (1.0 + x*x)); } mypi = h * sum; MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) printf("pi ist ungefaehr %.16f, Die Differenz %.16f\n", pi, fabs(pi - PIZahl)); } } MPI_Finalize(); return 0; }

Page 29: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

29

Ausgabe:

Eine Methode Pi zu berechnen, ist die Streifenmethode. Man unterteilt einen Viertelkreis in verschieden viele Streifenpaare. Je mehr Streifenpaare (in unserem Codebeispiel die Intervalle n) man nimmt, desto genauer wird später der Näherungswert von Pi. Der erste Streifen geht von dem Boden des Viertelkreises so hoch, bis er mit der ersten Ecke den Kreis berührt. Der äußere Streifen geht von dem Boden des Viertelkreises so hoch, dass die zweite Ecke gerade noch den Kreis berührt. Das Ganze sollte dann so aussehen:

________________ Wenn man nun alle Flächeninhalte der inneren Streifen zusammenzählt (blau markiert) und den aller äußeren Streifen (gruen markiert), hat man den Flächeninhalt und damit Pi schon bedeutend näher eingekreist. Zur Berechnung der Flächeninhalte lässt sich sagen, dass die Breite mit 1/n*r eindeutig ist, wobei n die Anzahl der Streifen(Intervalle) ist. Die Höhe lässt sich mit dem Satz des Pythagoras berechnen, wofür die roten schrägen Geraden eingezeichnet sind. Hierdurch hat man ein rechtwinkliges Dreieck von dem man zwei Seitenlängen kennt. Zum einen den Abstand vom Mittelpunkt des Kreises zu dem Streifen und zum anderen die Länge der roten Geraden, nämlich r. Das heißt, dass man mit Hilfe des Satzes von Pythagoras die dritte Seitenlänge ausrechnen kann. Die äußeren Streifen lassen sich leicht berechnen, da sie als Höhe die Höhe des links daneben liegenden inneren Rechtecks haben. Nun kann man die beiden Streifensortimente zusammenrechnen und wieder den Mittelwert bilden, so dass man einen relativ genauen Mittelwert des Flächeninhaltes des Viertelkreieses bekommt. Mit vier multipliziert erhält man den gesamten Flächeninhalt und somit die Zahl Pi bzw. eine Annäherung.

anika@anika:~/Desktop$ ./pi_c Anzahl der Intervalle: 9 pi ist ungefaehr 3.1426214565576127, Die Differenz 0.0010288029678196 Anzahl der Intervalle: 3 pi ist ungefaehr 3.1508492098656036, Die Differenz 0.0092565562758105 Anzahl der Intervalle: 2 pi ist ungefaehr 3.1623529411764704, Die Differenz 0.0207602875866773

Page 30: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

30

20. MPI – Funktionsübersicht A

o MPI_Abort o MPI_Address o MPI_Allgather o MPI_Allreduce o MPI_Alltoall o MPI_Alltoallv o MPI_Attr_delete o MPI_Attr_get o MPI_Attr_put

B o MPI_Barrier o MPI_Bcast o MPI_Bsend o MPI_Bsend_init o MPI_Buffer_attach o MPI_Buffer_detach

C o MPI_Cart_create o MPI_Cart_coords o MPI_Cart_get o MPI_Cart_map o MPI_Cart_shift o MPI_Cart_sub o MPI_Cartdim_get o MPI_Comm_compare o MPI_Comm_create o MPI_Comm_dup o MPI_Comm_free o MPI_Comm_group o MPI_Comm_rank o MPI_Comm_size o MPI_Comm_split

D o MPI_Dims_create

E o MPI_Errhandler_create o MPI_Errhandler_free o MPI_Errhandler_get o MPI_Errhandler_set o MPI_Error_string o MPI_Error_class

F o MPI_Finalize

G o MPI_Gather o MPI_Gatherv o MPI_Get_count o MPI_Get_elements

Page 31: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

31

o MPI_Graphdims_get o MPI_Graph_create o MPI_Graph_get o MPI_Graph_map o MPI_Graph_neighbors o MPI_Graph_neighbors_count o MPI_Group_compare o MPI_Group_difference o MPI_Group_excl o MPI_Group_free o MPI_Group_incl o MPI_Group_intersection o MPI_Group_range_excl o MPI_Group_range_incl o MPI_Group_rank o MPI_Group_size o MPI_Group_translate_ranks o MPI_Group_union

I o MPI_Ibsend o MPI_Init o MPI_Initialized o MPI_Intercomm_create o MPI_Intercomm_merge o MPI_Iprobe o MPI_Irecv o MPI_Irsend o MPI_Isend o MPI_Issend

K o MPI_Keyval_create o MPI_Keyval_free

O o MPI_Op_create o MPI_Op_free

P o MPI_Pack o MPI_Pack_size o MPI_Probe

R o MPI_Recv o MPI_Recv_init o MPI_Reduce o MPI_Reduce_scatter o MPI_Request_free o MPI_Rsend o MPI_Rsend_init

S o MPI_Scatter o MPI_Scatterv o MPI_Send

Page 32: MPI - cs.hs-rm.deweber/sysprog/proj09/MPI.pdf · MPI steht für Message Passing Interface und ist eine Realisierung eines Programmiermodells für die parallele Programmierung durch

32

o MPI_Sendrecv o MPI_Sendrecv_replace o MPI_Send_init o MPI_Ssend o MPI_Ssend_init o MPI_Start o MPI_Startall

T o MPI_Test o MPI_Testall o MPI_Testany o MPI_Testsome o MPI_Topo_test o MPI_Type_commit o MPI_Type_contiguous o MPI_Type_count o MPI_Type_extent o MPI_Type_free o MPI_Type_hindexed o MPI_Type_hvector o MPI_Type_indexed o MPI_Type_size o MPI_Type_struct o MPI_Type_vector

U o MPI_Unpack

W o MPI_Wait o MPI_Waitall o MPI_Waitany o MPI_Waitsome o MPI_Wtick o MPI_Wtime