Paralleles Programmieren mit MPI und OpenMP Vorlesung «Algorithmen für das wissenschaftliche Rechnen» 6.5.2002 Olaf Schenk, Michael Hagemann 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 1 Organisatorisches Teilnahme an den Übungen - Teilnahme ist verpflichtend für den Erhalt des Scheins - Gruppenarbeit in 2er- oder 3er-Gruppen - Praktikumsraum ist jeden Montag von 12-14 Uhr zugänglich - Alle zwei Wochen Besprechung des Aufgabenblattes Bitte eine Mail mit den Namen der Gruppenmitglieder bis Mittwoch an - michael.hagemann@unibas.ch - Zusammen mit Lösung der 1. Aufgabe, falls noch nicht abgegeben 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 2 1
Übersicht Motivation - Warum paralleles Rechnen Parallele Hardware - Auch hier: Speicher-Organisation Überblick OpenMP - Rechenmodell Einführung MPI - Rechenmodelle - Programmiermodelle 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 3 Motivation Wissenschaftliches Rechnen heisst immer öfter Paralleles Rechnen Einzelne Prozessoren stossen an physikalische Grenzen Netzwerke werden immer schneller Gesucht: Einheitliche Modelle und Methoden zur Programmierung paralleler und verteilter Systeme 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 4 2
Grundlegende Fragestellungen Hardware: - Wie schaltet man möglichst viele schnelle Gleitkomma- Einheiten zusammen? - Wie gewährleistet man schnellen Speicherzugriff für alle Prozessoren? - Bus-/Netzwerk-Technologie, Topologien - Sonderentwicklungen? (Vektorrechner, aktuell: Earth Simulator «Computenik») - Wirtschaftlichkeit (Benutzung marktüblicher Teile)? Software - Wie nutzt man die Hardware optimal? - Wie bleiben die Programme portabel? (für den nächsten Rechner) - Gute Kompromisse zwischen dem Aufwand der Parallelisierung und dem «Speedup» 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 5 Parallele Hardware (1) Einordnung nach Speicher-Organisation: Uniform Memory Access; Cache coherent (cc-uma) - SMPs (Shared Memory Parallel, ursprünglich: Symmetric Multi- Processors) - z.b. 2-CPUs Intel SMP Non-uniform MA; not cache coherent (ncc-numa) - MPPs (Massively Parallel Processing), Cluster - z.b. Cray T3E, Beowulf-Cluster Non-uniform MA; cache coherent (cc-numa) - «Virtual Shared Memory» - z.b.: SGI Origin, HP SuperDome - Verteilter Speicher, aber global addressierbar 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 6 3
Parallele Hardware (2) Cache-Kohärenz - Problem: Ein Prozessor liest ein Datum aus dem Speicher und verändert es (im Cache). Ein anderer tut das gleiche. Wenn der erste Proz. das Datum noch nicht in den Speicher zurückgeschrieben hat, ist das Ergebnis falsch. - Abhilfe: Cache/Bus-Snooping. Jeder Cache überprüft, ob andere Prozesse auf «seine» Inhalte zugreifen. Uniform Memory Access - Alle Speicherzugriffe sind gleich schnell - Der Speicher ist global adressierbar 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 7 Parallele Hardware (3) Shared Memory (cc-uma): Cache P1 Cache P1 P2 Memory Distributed Memory: (ncc-numa) Memory P1 Memory P1 P2 Network Distributed-Shared Memory: (cc-numa) Memory Cache P1 Memory Cache P1 P2 Network 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 8 4
Grenzen von Shared-Memory Eigentlich möchte man nur Shared-Memory haben Zur Zeit sind aber nur bis zu 16 Prozessoren praktikabel Wichtige Einschränkungen sind - Speicherbandbreite - Cache-Kohärenz Deshalb Trend zu Distributed-Shared Memory Systemen - MPP-Systeme mit Shared-Memory Knoten (z.b. IBM SP) - teilweise: cc-numa 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 9 Übersicht Motivation - Warum paralleles Rechnen Parallele Hardware - Auch hier: Speicher-Organisation Überblick OpenMP - Rechenmodell Einführung MPI - Rechenmodelle - Programmiermodelle 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 10 5
Rechenmodell: Threads Annahmen: - Ein Prozess kann aus mehreren Threads («Fäden») bestehen - Alle Threads teilen den Status (die Variablen) des Programms - Jeder Thread kann zusätzlich private Variablen haben - Die Threads können auf verschiedenen Prozessoren laufen - Es gibt Synchronisations- und Sperr-(Locking) Mechanismen 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 11 Was ist OpenMP? API zum Schreiben von «Multi-Threaded Applications» - Compiler-Direktiven und Bibliotheksfunktionen - Standardisiert für C und Fortran Neuer Standard (neuer Versuch einer Standardisierung) Wichtig, weil (fast) alle wichtigen Hersteller beteiligt Ansatz: - Programme werden schrittweise parallelisiert - Ausgangspunkt ist die serielle Version, die meist «erhalten» wird - Parallelität wird nicht direkt programmiert, sondern durch Direktiven beeinflusst 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 12 6
Einfaches Beispiel: Hallo Welt Das ursprüngliche Programm bleibt bestehen void main(void) { #pragma omp parallel { printf( Hallo Welt\n ); } } Ausführen (mit Umgebungsvariable): (~): export OMP_NUM_THREADS=4 (~):./hello-openmp Hallo Welt Hallo Welt Hallo Welt Hallo Welt 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 13 Parallele Regionen (Blöcke) Durch die Compiler-Direktive #pragma omg parallel wird der folgende Block parallel ausgeführt Es wird dafür ein «Team von Threads» gestartet - Die Anzahl ist abhängig von der Umgebungsvariablen OMP_NUM_THREADS, kann aber auch im Programm geändert werden - «Fork-Join-Parallelism» Nachdem alle Threads fertig sind, werden sie entweder gelöscht oder bleiben in Wartestellung 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 14 7
Parallele Schleifen Hauptanwendung von OpenMP ist es, Schleifen zu parallelisieren #pragma omp parallel for - Die direkt folgende Schleife wird parallelisiert Matrix-Matrix Multiplikation: #pragma omp parallel for for (i = 0; i < M; i++) for (j = 0; j < N; j++) for (k = 0; k < K; k++) C[i,j] = A[i,k] * B[k,j]; Die «i»-schleife wird von OMP_NUM_THREADS Threads gleichzeitig ausgeführt 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 15 Race Conditions Wenn mehrere Threads eine Variable lesen und schreiben, kann es zu Inkonsistenzen kommen Beispiel: Skalar-Produkt: sum = 0.0; #pragma omp parallel for for (i = 0; i < N; i++) sum = sum + x[i] * y[i]; Dies ist analog zu den Cache-Inkonsistenzen in der Hardware 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 16 8
Lösung 1: Locking Um die Addition vor anderen Threads zu schützen, kann man sie als «atomar» oder «kritisch» ausweisen sum = 0.0; #pragma omp parallel for for (i = 0; i < N; i++) #pragma omp critical { sum = sum + x[i] * y[i]; } Dies ist aber ineffizient, weil die Threads nicht mehr «parallel» arbeiten können 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 17 Lösung 2: Private Variablen Man kann daher in parallelen Regionen bestimmte Variablen als «privat» deklarieren: sum = 0.0; #pragma omp parallel private(local_sum) { local_sum = 0.0; #pragma omp parallel for for (i = 0; i < N; i++) local_sum = local_sum + x[i] * y[i]; #pragma omp critical { sum = sum + local_sum; } } (Dies lässt sich auch etwas knapper formulieren) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 18 9
Lösung 3: Reduction Variables Da solche Fälle typisch sind, kann man die «kritischen» Variablen als Reduction-Variables ausweisen In den Threads werden sie privat angelegt und am Ende der Schleife mit der entsprechenden Operation verknüpft sum = 0.0; #pragma omp parallel for reduction (+ : sum) for (i = 0; i < N; i++) sum = sum + x[i] * y[i]; 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 19 Übersicht OpenMP (1) Direktiven (in C, wie alles weitere): - #pragma omp [Klauseln ] - Werden von nicht-openmp Compilern überlesen Parallele Regionen (Blöcke) - #pragma omp parallel - Der folgende Block ( { } ) wird parallel ausgeführt Variable Scoping - #pragma omp private(..) shared(..) reduction(..) firstprivate(..) lastprivat(..) - Legt fest, welche Variablen gemeinsam genutzt werden und welche in jedem Thread als Kopie verwendet werden - shared ist der Default-Wert 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 20 10
Übersicht OpenMP (2) Synchronisierung - #pragma omp atomic, critical, ordered, barrier, flush - Essentiell für die Korrektheit der Programme Parallele Schleifen (work-sharing) - #pragma omp parallel for - Das nachfolgende «for» wird parallelisiert - Art der Aufteilung kann mit der schedule-klausel bestimmt werden - z.b. schedule(dynamic,4): Jeder Thread kriegt vier Schleifendurchläufe (1..4, 5..8, ) zugewiesen und bekommt neue, sobald er fertig ist - Andere Varianten sind static, guided und runtime 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 21 Übersicht OpenMP (3) Laufzeit-Umgebung: - Anzahl Prozessoren - omp_get_num_procs() - Anzahl Threads: - omp_set_num_threads(int); - int omp_get_num_threads(); - Entspricht der Variablen OMP_NUM_THREADS - int omp_get_thread_num(); 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 22 11
Übersicht OpenMP (4) - Dynamic-Mode: Dürfen in verschiedenen Blocks verschieden viele Threads erzeugt werden? - omp_set_dynamic, omp_get_dynamic - Entspricht: OMP_DYNAMIC (TRUE / FALSE) - Nesting: Dürfen in parallelen Regionen neue Thread-Teams erzeugt werden (verschachtelte Threads)? - omp_set_nested, omp_get_nested - Entspricht: OMP_NESTED (TRUE / FALSE) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 23 Einschätzung zu OpenMP Ermöglicht sehr leichte Parallelisierung von bestehenden Programmen Hat genügend Unterstützung um wirklicher Standard zu werden Beschränkt sich aber auf Shared-Memory Systeme Programmierung von Distributed-Memory Systemen verlangt mehr - Identifizierung einer optimalen Datenverteilung auf die Prozessoren (und angeschlossenen Speicher) Minimierung der Kommunikation - Noch nicht befriedigend durch Direktiven lösbar 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 24 12
Übersicht Vorlesung Motivation - Warum paralleles Rechnen Parallele Hardware - Auch hier: Speicher-Organisation Überblick OpenMP - Rechenmodell - Race Conditions Einführung MPI - Rechenmodell - Programmiermodelle 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 25 Rechenmodell: Message Passing Annahmen: - Es gibt eine Menge von Prozessen - Prozesse laufen nebenläufig - Prozesse können untereinander Nachrichten austauschen (evtl. über Umwege) - An der Kommunikation sind beide Seiten beteiligt (kooperativ, explizites Empfangen) «Schwächer» als Thread-Modell, dafür allgemeingültiger 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 26 13
Vorteile des Message Passing Modells Allgemeingültigkeit - Von Shared-Memory bis E-Mail Ausdruckskraft - Datentypen, kollektive Kommunikation - «Anthropomorpher» Ansatz («Ich schicke Dir...») Relativ einfach zu «Debuggen» - Kommunikation immer explizit Performance - Optimierung auf verschiedene Plattformen möglich 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 27 Nachteile Das Programm ist nicht wiederzuerkennen Erfordert hohen Aufwand und Fachwissen (PhD- Programming) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 28 14
Was ist MPI? Message Passing Interface Syntaktische und semantische Spezifikation von Bibliotheksfunktionen zum Nachrichtenaustausch (ursprünglich für Fortran, C und C++) - Für verschiedene Plattformen gibt es verschiedene Implementierungen dieser Funktionen MPI Explizites Programmieren von Kommunikation 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 29 Bereiche von MPI MPI basiert auf wenigen Konzepten, die kombiniert werden können. Direkte Kommunikation («Punkt-zu-Punkt») Kollektive Kommunikation («Alle-mit-Allen») Synchronisation Gruppierung von Prozessen 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 30 15
Einstieg: hallo-welt (1) int main(int argc, char* argv[]) { int my_rank; /* Rang *dieses* Prozesses */ int num_procs; /* Prozesse insgesamt */ MPI_Init(&argc, &argv);/* MPI initialisieren */ MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); printf("ich bin der %d. von %d Prozessen.\n", my_rank, num_procs); } MPI_Finalize(); /* MPI beenden */ 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 31 hallo-welt (2) Starten des Programms in n Prozessen: (~): mpicc -o hallo-welt hallo-welt.c (~): mpirun -np 2 hallo-welt Ich bin der 0. von 2 Prozessen. Ich bin der 1. von 2 Prozessen. (~): mpirun -np 4 hallo-welt Ich bin der 2. von 4 Prozessen. Ich bin der 1. von 4 Prozessen. Ich bin der 3. von 4 Prozessen. Ich bin der 0. von 4 Prozessen. Reihenfolge der Ausgabe steht nicht fest! 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 32 16
Programmier-Modell SPMD «Single Program Multiple Data» Es gibt nur ein Programm, das aber in n Prozessen gleichzeitig gestartet wird - Normalerweise auf n Prozessoren (nicht notwendig) Jeder Prozess arbeitet auf seinen Daten (my_rank) Der Programmablauf kann unterschiedlich sein (meist abhängig von der Prozessnummer) Andere Modelle: MPMD: Multiple Programs Multiple Data - Oft: Master Slaves, Frontend Backends 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 33 Grundlegende Funktionen: Senden MPI_Send(buffer, length, type, destination, tag, communicator) - buffer: Adresse des Puffers - type: MPI-Datentyp (z.b. MPI_INT) - destination: Nummer (Rang, Id) des Ziels - tag: Zusätzliche Identifikation der Nachricht - communicator: Menge der beteiligten Prozesse - Alle Prozesse: MPI_COMM_WORLD Blockiert, bis Übertragung tatsächlich beginnt 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 34 17
Grundlegende Funktionen: Empfangen MPI_Recv(buffer, length, type, source, tag, comm, status) - source: Von wem wird die Nachricht erwartet? - egal: MPI_SOURCE_ANY - status: Wie ist die Nachricht angekommen? - Enthält immer: MPI_SOURCE, MPI_TAG und MPI_ERROR Blockiert, bis vollständig empfangen 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 35 int char MPI_Status my_rank, num_procs; message[20]; status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); MPI_Comm_size(MPI_COMM_WORLD, &num_procs); if (my_rank == 0) { strcpy(message, "Grüezi!"); MPI_Send(&message, strlen(message)+1, MPI_CHAR, 1, 77, MPI_COMM_WORLD); } if (my_rank == 1) { MPI_Recv(&message, 20, MPI_CHAR, 0, 77, MPI_COMM_WORLD, &status); printf("received: %s from %d\n", message, status.mpi_source); } MPI_Finalize(); 18
Datentypen in MPI Erhöhen die Portabilität - Verschiedene Repräsentationen auf verschiedenen Plattformen (Grösse, Big/Little-Endian) Erlauben Rechenoperationen (reduce) Zusammengesetzte Datentypen - Array-Spalten und -Zeilen, Vektoren - Zusammengesetzte Strukturen - z.b.: komplexe Zahlen: MPI_Datatype *complex; MPI_Type_contiguous(2, MPI_DOUBLE, complex); MPI_Type_commit(complex); 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 37 Synchronisation Explizit: Barriere. Alle beteiligten Prozesse warten, bis jeder an dieser Barriere angekommen ist - MPI_Barrier(MPI_Comm comm) Implizit: Blockierendes Empfangen. Der Prozess läuft erst dann weiter, wenn alle Daten empfangen wurden - MPI kennt auch nicht-blockierendes Empfangen 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 38 19
Gruppierung von Prozessen Aufteilung der Prozesse in Gruppen - Verschiedene Programmteile können ungestört voneinander arbeiten - Wichtig für Bibliotheken, die ungestört kommunizieren müssen - Jeder Programmteil erhält (definiert sich) eigenen Communicator Anordnung der Prozesse in Topologien - Abbildung der Prozesse auf die zugrundeliegende Topologie des Netzwerks - Sind die Nachbarn physikalisch miteinander verbunden? - Wie heissen die Nachbar-Prozesse? (Numerierung) - Topologie die für Algorithmus optimal ist (MPI sucht dann Abbildung auf Netzwerk) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 39 Beispiele für Prozess-Topologien Feld (Array) (0,0) (0) (0,1) (1) (0,2) (2) (0,3) (3) Gitter (Grid) (0,0) (0,1) (0,2) (0,3) (1,0) (1,1) (1,2) (1,3) (2,0) (2,1) (2,2) (2,3) Würfel (Cube) (0,1,0) (0,1,1) (1,1,1) (0,0,1) (1,0,1) (0,0,0) (1,1,0) (1,0,0) (3,0) (3,1) (3,2) (3,3) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 40 20
Kollektive Kommunikation Alle Prozesse beteiligen sich an der Kommunikation Verteilung von Daten: - broadcast: Verteile ein Datum vollständig an alle Prozesse - scatter: Verteile ein Datum in Teilen an alle Prozesse - gather: Sammle die Teile in einem Prozess Berechnung: - reduce: Verknüpfe Daten mit kommutativer Operation, z.b.: +, *, max, min 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 41 Beispiel: Eine Zahl an Alle int my_rank, data; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); if (my_rank == 0) data = 17; MPI_Broadcast(&data, 1, MPI_INT, 0, /* Sender (Root) */ MPI_COMM_WORLD); if (my_rank!= 0) printf("received: %d \n", data); 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 42 21
Warum kollektive Kommunikation? Höhere Ausdrucksebene Vereinfacht oft den Code Effizienter (z.b. durch Baumalgorithmen): 17... N Transfers 17 1 2 2log 2 (N) Tr. 1 2... N 3 4... 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 43 Kollektive Kommunikation (scatter) kollektiv: MPI_Scatter (send_buf, 4, MPI_INT, recv_buf, 4, MPI_INT, 0, comm); Prozess 0 send_buf (Root-Prozess 0) 0 1 2 3 recv_buf (in allen Prozessen) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 44 22
Beispiel: α * Vektor 1. Verteilung des Vektors und des Skalars int dim_local = dim / P; double vec[dim]; double vec_local[dim_local]; double alpha; if (my_rank == 0) { /* Initialisiere vec und alpha*/ } MPI_Broadcast (alpha, 1, MPI_Double, 0, MPI_COMM_WORLD); MPI_Scatter(vec, dim_local, MPI_Double, vec_local, dim_local, MPI_Double, 0, MPI_COMM_WORLD); 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 45 Beispiel: α * Vektor (2) 2. Berechnung der lokalen Produkte for (a = 0; a < dim_local; a++) vec_local[a] *= alpha; 3. Sammlung des Vektors im Root-Prozess: MPI_Gather(vec, dim_local, MPI_Double, vec_local, dim_local, MPI_Double, 0, MPI_COMM_WORLD); 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 46 23
Nicht-Blockierende Kommunikation Wichtig um nebenläufig zu Senden Überlappung von Berechnung und Kommunikation Prinzip: Aufteilen in Initiierung und Statusabfrage MPI_Send kann aufgeteilt werden in - MPI_ISend: Startet die Übertragung («Immediate») - MPI_Wait: Wartet auf die vollständige Übertragung (blockiert) MPI_ISend(buffer, length, type, destination, tag, comm, request) - request: Transaktions-Nummer - Warten auf Beendigung mit MPI_Wait, MPI_Waitall, MPI_Waitsome oder MPI_Waitany 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 47 Communication Modes Sind «orthogonal» zum nicht-blockierenden Senden (z.b.: MPI_Rsend und MPI_Irsend) Standard - «nicht-lokal» Wartet auf ein Receive des Empfängers Buffered - Daten werden vor dem Senden kopiert, «lokal» Synchronous - Wartet, bis Empfänger anfängt zu Empfangen Ready - MPI_Rrecv vor MPI_Rsend ( kein Handshake) 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 48 24
Communication Modes (2) Der Standard-Modus bietet in den meisten Implementierungen den besten Kompromiss Gepuffertes Senden kann helfen, Deadlocks zu vermeiden - Verschlechtert aber oft Performance (Durchsatz) Besser: Nicht-Blockierendes Senden 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 49 Freie Implementierungen MPICH - Argonne National Lab, Mississippi State Univ. - http://www.mcs.anl.gov/mpi/mpich - Sowohl für Cluster als auch für MPPs LAM (Local Area Multicomputer) - Ohio Supercomputer Center - http://www.mpi.nd.edu/lam - Nicht für MPPs verfügbar - Teile von MPI-2 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 50 25
Literatur Offizielle Seite zu OpenMP: http://www.openmp.org Offizielle Seite zu MPI: http://www.mpi-forum.org - Dokumentation zu MPI-1.1 W. Gropp, E. Lusk und A. Skjellum. Using MPI. Portable Parallel Programming with the Message Passing Interface. MIT Press, 1995. The History of Parallel Computing: http://ei.cs.vt.edu/~history/parallel.html Survey of High Performance Computing Systems: http://nhse.npac.syr.edu/hpccsurvey Top 500 Supercomputers: http://www.top500.org 6.5.2002 Algorithmen des wissenschaftlichen Rechnens 51 26