320 likes | 477 Vues
OpenMP. Präsentation im Rahmen des Seminars „Parallele und verteilte Programmierung“ Michael Westermann. Gliederung. Einführung Vergleich von OpenMPI und MPI Grundlagen Parallelisierung von Programmbereichen Koordination und Synchronisation von Threads Zusammenfassung. Einführung.
E N D
OpenMP Präsentation im Rahmen des Seminars „Parallele und verteilte Programmierung“ Michael Westermann
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Koordination und Synchronisation von Threads • Zusammenfassung
Einführung • OpenMP: „Open specifications for Multi Processing“ • Spezifikation für parallele Programmierung • Multiprozessor-Systeme • Gemeinsamer Speicher • Möglichkeit, ein Programm schrittweise zu parallelisieren • Compiler-Direktiven, Bibliotheksfunktionen, Umgebungsvariablen • Bindings für C, C++ und Fortran • 1997 für Fortran; 1998 für C/C++ • Aktuelle Version: OpenMP 2.5 (Mai 2005) • Von vielen Soft- und Hardwareherstellern unterstützt (Intel, Sun, Compaq usw.)
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Koordination und Synchronisation von Threads • Zusammenfassung
Vergleich OpenMP vs. MPI • OpenMP und MPI können auch kombiniert werden: • MPI verteilt Arbeit auf Multiprozessor-Systeme • OpenMP führt die Arbeit dort parallel aus
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Gemeinsamer Speicher • Programmiermodell • OpenMP-Direktiven • Parallelisierung von Programmbereichen • Koordination und Synchronisation von Threads • Zusammenfassung
Grundlagen: Gemeinsamer Speicher • Gemeinsamer Speicher • Mehrere Prozessoren • Einheitlicher Adressraum • Verteilter gemeinsamer Speicher • Mehrere Prozessoren • Seitenbasierter, virtueller, gemeinsamerAdressraum • Beide Varianten werden von OpenMP unterstützt
Grundlagen: Programmiermodell • Auf Threads basierend • Ausführungsfäden innerhalb eines Prozesses • Leichtgewichtiger als Prozesse • Gemeinsamer Adressraum, zusätzlich eigener Stack • Kommunikation über gemeinsame Variablen • Fork-join-Prinzip • Master-Thread erzeugt weitere Threads • Parallele Ausführung des Bereichs • Synchronisation der Threads am Ende • Beenden der Slave-Threads
OpenMP-Direktiven • Einbinden der Datei omp.h zu Beginn • Parallelisierung mittels Compiler-Direktiven #pragmaomp<Klausel> • Direktive wird ignoriert, wenn Compiler OpenMP nicht unterstützt • Programm wird seriell ausgeführt • Identischer Quelltext für Ein- und Multiprozessor-System
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Parallele Bereiche • Parallelisierung von Schleifen • Parallelisierung unabhängiger Abschnitte • Koordination und Synchronisation von Threads • Zusammenfassung
Parallele Bereiche • parallel-Direktive: #pragmaomp parallel [Parameter [, Parameter]…] { Anweisungsblock } • Grundlegendes Konstrukt • Threads arbeiten Anweisungsblock mit gemeinsamen oder privaten Variablen ab (singleprogram multiple data, SPMD) • Synchronisation am Ende des parallelen Bereichs • Parallele Bereiche können geschachtelt werden • Parameter: • Bestimmung der Thread-Anzahl ( num_threads(x)) • Variablendeklarationen (gemeinsame vs. private Variablen)
Parameter: Variablendeklarationen • shared(<Liste_Variablen>) • Gemeinsame Variablen der Threads Lesen und Schreiben findet auf gleichem Datenbereich statt • private(<Liste_Variablen>) • Jeder Thread erhält uninitialisierte Kopie der Variablen Nur der jeweilige Thread kann diese Lesen und Schreiben • default(shared | private | none) • shared: Variablen sind standardmäßig gemeinsam • private: Variablen sind standardmäßig privat • none: Alle Variablen müssen explizit gekennzeichnet werden • Weitere Variablendeklarationen: • firstprivate, lastprivate, copyin, reduction
Parallele Bereiche: Beispiel #include<omp.h> intnummer; intmain() { #pragmaomp parallel private(nummer) num_threads(4) { // Nummer des aktuellen Threads nummer = omp_get_thread_num(); printf("Thread-Nummer: ",nummer); } } Mögliche Ausgabe: Thread-Nummer: 0 Thread-Nummer: 2 Thread-Nummer: 1 Thread-Nummer: 3
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Parallele Bereiche • Parallelisierung von Schleifen • Parallelisierung unabhängiger Abschnitte • Koordination und Synchronisation von Threads • Zusammenfassung
Parallelisierung einer for-Schleife Speicher A[1] 4 Prozessoren: for(i=1,i<=25,i++) a[i] = b[i] + c[i] A[100] for(i=26,i<=50,i++) a[i] = b[i] + c[i] B[1] for(i=1,i<=100,i++) a[i] = b[i] + c[i] for(i=51,i<=75,i++) a[i] = b[i] + c[i] B[100] C[1] for(i=76,i<=100,i++) a[i] = b[i] + c[i] C[100]
Parallelisierung einer for-Schleife • Work-Sharing-Konstrukt • Verteilung der Iterationen auf mehrere Threads • Jede Iteration wird von genau einem Thread ausgeführt • for-Direktive: #pragmaompfor[Parameter…] for (Index=Startwert; Test; Inkrementierung) { Schleifenrumpf } • Voraussetzungen: • Iterationen unabhängig voneinander • Anzahl Iterationen vor Ausführung bestimmbar • Innerhalb eines parallelen Bereichs (oder kombinierte Direktive) • #pragmaompparallel for[Parameter…]
Parallelisierung einer for-Schleife for (i=Startwert; Test; Inkrementierung) Anforderungen an Schleifenkopf: • i: Variable vom Typ int • Startwert: x • Test: i op x , mit op { <, ≤, >, ≥ } • Inkrementierung: ++i, --i, i++, i--,i += x, i -= x, i = i + x, i = i - x • x: Schleifenunabhängiger Integer-Ausdruck
Parameter for-Direktive: schedule • Steuert Aufteilung der Iterationen auf die Threads • Lastverteilung • schedule(static, block_size) • Iterationen werden in Blöcke der Größe block_size zusammengefasst • Verteilung der Blöcke auf die Threads bereits vor Ausführung • schedule(dynamic, block_size) • Iterationen werden in Blöcke der Größe block_size zusammengefasst • Nach Bearbeitung eines Blockes erhält Thread neuen Block • schedule(guided, block_size) • Exponentiell abnehmendeBlockgröße im Zeitverlauf • Beispiel: 64 Iterationen, 2 Threads, block_size=4: 1. 64/2=32, 2. 32/2=16, 3. 16/2=8, 4. 8/2=4, 5. 4
Beispiel for-Direktive: Primzahlenausgabe #include<stdio.h> #include <omp.h> intmain() { intzahl, teiler, treffer; printf ("Primzahlen: \n"); #pragmaomp parallel for private(teiler,treffer) \ schedule(dynamic,100) for(zahl = 2; zahl < 100000; zahl++) { treffer = 0; #pragmaomp parallel for private(teiler, treffer) // Überprüfung ob 2 bis zahl Teiler von zahl for(teiler = 2; teiler < zahl; teiler++) { if(zahl % teiler == 0) { treffer = 1; } } if(treffer == 0) { printf ("%d, ", zahl); } } } Mögliche Ausgabe: Primzahlen: 2, 3, 5, 7, 11, 13, 101, 19, 23, 113, 29, […], 99991,
Parameter for-Direktive: ordered • Ausführung erst, wenn alle vorherigen Iterationen den Anweisungsblock beendet haben • ordered-Parameter der for-Direktive hinzufügen • ordered-Direktive vor entsprechenden Anweisungsblock:#pragmaompordered { Anweisungsblock }
Beispiel: Primzahlenausgabe, aufsteigend #include<stdio.h> #include<omp.h> intmain() { intzahl, teiler, treffer; printf ("Primzahlen: \n"); #pragmaomp parallel for private(teiler, treffer) \ schedule(static,1) ordered for(zahl = 2; zahl < 100000; zahl++) { treffer = 0; // Überprüfung ob 2 bis zahl Teiler von zahl for(teiler = 2; teiler < zahl; teiler++) { if(zahl % teiler == 0) { treffer = 1; } } #pragmaompordered if(treffer == 0) { printf ("%d, ", zahl); } } } Ausgabe: Primzahlen: 2, 3, 5, 7, 11, 13, 19, 23, 29, 31, 37, 41, 43, 47, […], 99991,
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Parallele Bereiche • Parallelisierung von Schleifen • Parallelisierung unabhängiger Abschnitte • Koordination und Synchronisation von Threads • Zusammenfassung
Parallelisierung unabhängiger Abschnitte • Work-Sharing-Konstrukt • Abschnitte werden auf Threads verteilt • Jeder Abschnitt wird von genau einem Thread ausgeführt • sections-Direktive:#pragma omp sections [ Parameter [, Parameter …] ] { [ #pragma omp section { Anweisungsblock_1 } ] [ #pragma omp section { Anweisungsblock_2 } ] } • Abschnitte müssen unabhängig voneinander sein • section-Direktive nur innerhalb der sections-Direktive
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Koordination und Synchronisation von Threads • Kritische Abschnitte • Atomare Operationen • Synchronisation • Ausführung ausschließlich des Master-Threads • Zusammenfassung
RaceConditions • Beispiel: • A = 0 • 2 Threads führen parallel aus: A = A + 1 • Mögliche Ergebnisse: • A = 2 • A = 1 • Ergebnis hängt vom zeitlichen Ablauf der Operationen ab • Lösungsmöglichkeiten: • Kritische Abschnitte • Atomare Operationen
Kritische Abschnitte • Wechselseitiger Ausschluss (mutual exclusion) • Abschnitte werden zu kritischen Abschnitten deklariert • Höchstens ein Thread darf gleichzeitig im kritischen Abschnitt mit gleichem name sein • critical-Direktive:#pragma omp critical [(name)] { kritischer_Abschnitt }
Atomare Operationen • Zuweisung wird „am Stück“ (atomar = unteilbar) ausgeführt • atomic-Direktive:#pragma omp atomic Zuweisung • Zuweisung darf folgende Form haben: • x++, x- - • ++x, - - x • x binop= skalarer_Ausdruck • binop { +, -, *, /, &, ^, |, <<, >> } • skalarer_Ausdruck darf nicht auf x referenzieren und ist nicht Teil der atomaren Operation
Synchronisation von Threads • barrier-Direktive:#pragma omp barrier • Thread setzt Ausführung erst fort, wenn alle Threads die barrier-Direktive erreicht haben • Bezieht sich nur auf Threads des eigenen „Teams“ • Direktive muss von allen oder von keinem Thread erreicht werden • Sonst: Verklemmung (Deadlock)
Ausführung nur durch Master-Thread • Master-Direktive:#pragma omp master { Anweisungsblock } • Anweisungsblock wird ausschließlich von Master-Thread bearbeitet • Bei verschachtelter Parallelisierung:Master-Thread des innersten parallelen Bereichs • Alle anderen Threads ignorieren den Block
Gliederung • Einführung • Vergleich von OpenMPI und MPI • Grundlagen • Parallelisierung von Programmbereichen • Koordination und Synchronisation von Threads • Zusammenfassung
Zusammenfassung • Einheitlicher Standard für Programmierung von Parallelrechnern mit gemeinsamem Speicher • Ermöglicht leichte Parallelisierung bestehender Programme (inkrementelle Parallelisierung) • Parallelisierung mittels Compiler-Direktiven • Fork-join-Prinzip • Unterstützung namhafter Hersteller • http://www.openmp.org/