Synchronisation

Werbung
Synchronisation
Während in den parallelisierten Abschnitten eines OpenMP Programms die Threads die ihnen zugeteilten Berechnungen normalerweise voneinander unabhängig ausführen sollen, ist es
manchmal notwendig, entweder die Reihenfolge festzulegen, in der die Threads gewisse Programmabschnitte abarbeiten, oder Haltepunkte einzubauen, an denen gewartet werden muß,
bis alle Threads eine bestimmte Stelle im Code erreicht haben.
Einen derartigen Synchronisationspunkt im engeren Sinn definert die barrier Direktive
!$omp barrier
bzw.
#pragma omp barrier
die in allen Threads einer Parallel Region vorkommen muß und bewirkt, daß kein Thread in der
Ausführung des Programms fortfahren darf, bevor die anderen Mitglieder des Teams ebenfalls
diese Stelle erreicht haben. Implizit findet eine solche Synchronisation der Threads am Ende
jeder do/for, sections und single Direktive statt, kann aber dort durch die nowait Option
verhindert werden. In diesem Fall wird es meist notwendig sein, die Threads später durch eine
explizite barrier Direktive wieder zu synchronisieren.
Soll eine Synchronisation “per Hand”, z.B. durch das Setzen und Testen globaler Variablen
(Flags), vorgenommen werden, so dient die flush Direktive
!$omp flush [(list)]
bzw.
#pragma omp flush [(list)]
dazu, sicherzustellen, daß alle vor Ausführung der Direktive befindlichen Speicheroperationen
tatsächlich abgeschlossen und noch keine der auf die Direktive folgenden begonnen wurden. Ist
explizit eine Liste list von Variablen angegeben, so bezieht sich das nur auf die in dieser Liste
angeführten Speicherstellen, sonst auf alle “shared” Variablen.
Um einen Programmabschnitt vor dem gleichzeitigen Zugriff durch mehr als einen Thread zu
schützen, steht die critical Direktive zur Verfügung, deren allgemeine Form
!$omp critical [(lock)]
critical block
!$omp critical [(lock)]
12
bzw.
#pragma omp critical [(lock)]
critical block
lautet. Sie bewirkt, daß der eingeschlossene Abschnitt zwar von allen Threads, aber immer
nur von einem Thread auf einmal abgearbeitet wird. Gibt es in einem Programm verschiedene
“kritische Abschnitte”, die einander logisch nicht ausschließen (und daher sehr wohl gleichzeitig
ausgeführt werden können), so kann man sie durch verschiedene Namen (Locks) voneinander
unterscheiden. Die Einschränkung der Ausführung besteht dann nur darin, daß nie mehr als
ein Thread in jedem durch lock geschützten Abschnitt aktiv sein darf.
Oft besteht ein kritischer Abschnitt nur aus einer einzigen Anweisung, durch die der Wert
einer skalaren Variablen auf einfache Weise verändert wird. Da exklusiver Zugriff+Modifikation
von Speicherstellen auf manchen Computerarchitekturen auf Hardwareniveau unterstützt wird
(und daher von OpenMP dort nicht in Software emuliert werden sollte), steht für derartige
Operationen als portabler Mechanismus die atomic Direktive
!$omp atomic
var=var operator expr
oder
!$omp atomic
var=intrinsic(var,expr)
bzw.
#pragma omp atomic
var=var operator expr;
oder
#pragma omp atomic
var operator=expr;
zur Verfügung, wobei in Fortran-90 für operator und intrinsic
+,-,*,/,IAND,IOR,IEOR,.AND.,.OR.,.EQV.,.NEQV.,MAX,MIN
und in C
+,-,*,/,&,^,|,<<,>>
13
(dort aber auch Statements der Form var++; var--; usw.) in Frage kommen.
Als Alternative zur critical Direktive besteht auch die Möglichkeit, den exklusiven Zugriff
der Threads auf gemeinsame Variablen explizit durch Locks zu steuern.1 Dafür sind folgende
Prozeduren der Laufzeitbibliothek vorgesehen
Fortran-90
C
omp_init_lock(lock)
omp_destroy_lock(lock)
omp_set_lock(lock)
omp_unset_lock(lock)
logical omp_test_lock(lock)
void omp_init_lock(omp_lock_t *lock)
void omp_destroy_lock(omp_lock_t *lock)
void omp_set_lock(omp_lock_t *lock)
void omp_unset_lock(omp_lock_t *lock)
int omp_test_lock(omp_lock_t *lock)
wobei in Fortran—je nach Betriebssystem—die Variable lock (Typ integer, der Name des
Locks) genug Speicherplatz bieten muß, um eine Adresse aufnehmen zu können; in C ist lock
ein Pointer vom Typ omp lock t, der in omp.h definiert ist. Die Prozeduren omp init lock
und omp destroy lock erzeugen bzw. löschen ein Lock. Mit omp set lock kann ein Thread ein
Lock setzen und sich damit z.B. den exklusiven Zugriff auf einen Speicherbereich reservieren
(die übrigen Threads müssen währenddessen mit omp test lock prüfen, ob sie auf diese Daten
zugreifen dürfen) und danach mit omp unset lock wieder freigeben.
1
Mutex=mutually exclusive synchronization.
14
Herunterladen