Freigeben über


2. Direktiven

Direktiven basieren auf #pragma Richtlinien, die in den C- und C++-Standards definiert sind. Compiler, die die OpenMP C- und C++-API unterstützen, enthalten eine Befehlszeilenoption, die alle OpenMP-Compilerdirektiven aktiviert und ermöglicht.

2.1 Richtlinienformat

Die Syntax einer OpenMP-Direktive wird formell durch die Grammatik in Anhang C und informell wie folgt angegeben:

#pragma omp directive-name  [clause[ [,] clause]...] new-line

Jede Direktive beginnt mit #pragma omp, um das Konfliktpotenzial mit anderen Pragmadirektiven (Nicht-OpenMP- oder Lieferantenerweiterungen auf OpenMP) mit denselben Namen zu verringern. Der Rest der Direktive folgt den Konventionen der C- und C++-Standards für Compilerdirektiven. Insbesondere kann Leerzeichen vor und nach dem #Leerzeichen verwendet werden, und manchmal muss Leerzeichen verwendet werden, um die Wörter in einer Direktive zu trennen. Vorverarbeitungstoken nach dem #pragma omp Ersetzen von Makros.

Bei Direktiven wird zwischen Groß- und Kleinschreibung unterschieden. Die Reihenfolge, in der Klauseln in Direktiven angezeigt werden, ist nicht relevant. Klauseln zu Direktiven können nach Bedarf wiederholt werden, vorbehaltlich der in der Beschreibung der einzelnen Klauseln aufgeführten Einschränkungen. Wenn eine Variable-Liste in einer Klausel angezeigt wird, darf nur Variablen angegeben werden. Pro Direktive kann nur ein Direktivenname angegeben werden. Die folgende Direktive ist beispielsweise nicht zulässig:

/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier

Eine OpenMP-Direktive gilt für höchstens eine erfolgreiche Anweisung, die ein strukturierter Block sein muss.

2.2 Bedingte Kompilierung

Der _OPENMP Makroname wird durch OpenMP-kompatible Implementierungen als dezimale Konstante yyyymm definiert, die das Jahr und den Monat der genehmigten Spezifikation ist. Dieses Makro darf weder Gegenstand einer #define#undef Vorverarbeitungsdirektive sein.

#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif

Wenn Anbieter Erweiterungen für OpenMP definieren, können sie zusätzliche vordefinierte Makros angeben.

2.3 paralleles Konstrukt

Die folgende Direktive definiert eine parallele Region, bei der es sich um eine Region des Programms handelt, die von vielen Threads parallel ausgeführt werden soll. Diese Direktive ist das grundlegende Konstrukt, das die parallele Ausführung startet.

#pragma omp parallel [clause[ [, ]clause] ...] new-line   structured-block

Die Klausel ist eine der folgenden:

  • if(Skalarausdruck)
  • private(Variable-Liste)
  • firstprivate(Variable-Liste)
  • default(shared | none)
  • shared(Variable-Liste)
  • copyin(Variable-Liste)
  • reduction(Operatorvariablenliste:)
  • num_threads(ganzzahliger Ausdruck)

Wenn ein Thread zu einem parallelen Konstrukt gelangt, wird ein Team von Threads erstellt, wenn einer der folgenden Fälle zutrifft:

  • Es ist keine if Klausel vorhanden.
  • Der if Ausdruck wird zu einem Wert ungleich Null ausgewertet.

Dieser Thread wird zum Masterthread des Teams mit einer Threadnummer von 0, und alle Threads im Team, einschließlich des Masterthreads, führen den Bereich parallel aus. Wenn der Wert des if Ausdrucks null ist, wird der Bereich serialisiert.

Um die Anzahl der angeforderten Threads zu ermitteln, werden die folgenden Regeln in der Reihenfolge berücksichtigt. Die erste Regel, deren Bedingung erfüllt ist, wird angewendet:

  1. Wenn die num_threads Klausel vorhanden ist, ist der Wert des ganzzahligen Ausdrucks die Anzahl der angeforderten Threads.

  2. Wenn die omp_set_num_threads Bibliotheksfunktion aufgerufen wurde, ist der Wert des Arguments im zuletzt ausgeführten Aufruf die Anzahl der angeforderten Threads.

  3. Wenn die Umgebungsvariable OMP_NUM_THREADS definiert ist, ist der Wert dieser Umgebungsvariable die Anzahl der angeforderten Threads.

  4. Wenn keine der oben genannten Methoden verwendet wird, wird die Anzahl der angeforderten Threads implementierungsdefiniert.

Wenn die num_threads Klausel vorhanden ist, ersetzt sie die Anzahl der Threads, die von der omp_set_num_threads Bibliotheksfunktion oder der OMP_NUM_THREADS Umgebungsvariablen angefordert werden, nur für den parallelen Bereich, auf den sie angewendet wird. Spätere parallele Regionen sind davon nicht betroffen.

Die Anzahl der Threads, die den parallelen Bereich ausführen, hängt auch davon ab, ob die dynamische Anpassung der Anzahl der Threads aktiviert ist. Wenn die dynamische Anpassung deaktiviert ist, führt die angeforderte Anzahl von Threads den parallelen Bereich aus. Wenn die dynamische Anpassung aktiviert ist, ist die angeforderte Anzahl von Threads die maximale Anzahl von Threads, die den parallelen Bereich ausführen können.

Wenn ein paralleler Bereich auftritt, während die dynamische Anpassung der Anzahl der Threads deaktiviert ist und die Anzahl der für den parallelen Bereich angeforderten Threads mehr ist als die Anzahl, die das Laufzeitsystem bereitstellen kann, ist das Verhalten des Programms implementierungsdefiniert. Eine Implementierung kann z. B. die Ausführung des Programms unterbrechen oder den parallelen Bereich serialisieren.

Die omp_set_dynamic Bibliotheksfunktion und die OMP_DYNAMIC Umgebungsvariable können verwendet werden, um die dynamische Anpassung der Anzahl von Threads zu aktivieren und zu deaktivieren.

Die Anzahl der physischen Prozessoren, die die Threads zu einem bestimmten Zeitpunkt hosten, ist implementierungsdefiniert. Nach der Erstellung bleibt die Anzahl der Threads im Team für die Dauer dieses parallelen Bereichs konstant. Sie kann entweder explizit vom Benutzer oder automatisch vom Laufzeitsystem von einem parallelen Bereich in einen anderen geändert werden.

Die Anweisungen, die innerhalb des dynamischen Umfangs des parallelen Bereichs enthalten sind, werden von jedem Thread ausgeführt, und jeder Thread kann einen Pfad von Anweisungen ausführen, die sich von den anderen Threads unterscheiden. Richtlinien außerhalb des lexikalischen Umfangs einer parallelen Region werden als verwaiste Richtlinien bezeichnet.

Am Ende einer parallelen Region gibt es eine konkludente Barriere. Nur der Masterthread des Teams setzt die Ausführung am Ende eines parallelen Bereichs fort.

Wenn ein Thread in einem Team, in dem eine parallele Region ausgeführt wird, ein weiteres paralleles Konstrukt auftritt, erstellt es ein neues Team und wird zum Meister dieses neuen Teams. Geschachtelte parallele Regionen werden standardmäßig serialisiert. Daher wird standardmäßig ein geschachtelter paralleler Bereich von einem Team ausgeführt, das aus einem Thread besteht. Das Standardverhalten kann mithilfe der Laufzeitbibliotheksfunktion omp_set_nested oder der Umgebungsvariable OMP_NESTEDgeändert werden. Die Anzahl der Threads in einem Team, die einen geschachtelten parallelen Bereich ausführen, ist jedoch implementierungsdefiniert.

Einschränkungen für die parallel Richtlinie lauten wie folgt:

  • Höchstens kann eine if Klausel in der Direktive angezeigt werden.

  • Es ist nicht angegeben, ob Nebenwirkungen innerhalb des Ausdrucks oder num_threads Ausdrucks auftreten.

  • Ein throw innerhalb eines parallelen Bereichs ausgeführtes Element muss dazu führen, dass die Ausführung innerhalb des dynamischen Umfangs desselben strukturierten Blocks fortgesetzt wird, und er muss vom gleichen Thread abgefangen werden, der die Ausnahme ausgelöst hat.

  • Nur eine einzelne num_threads Klausel kann in der Direktive angezeigt werden. Der num_threads Ausdruck wird außerhalb des Kontexts des parallelen Bereichs ausgewertet und muss zu einem positiven ganzzahligen Wert ausgewertet werden.

  • Die Reihenfolge der Auswertung der if Klauseln ist num_threads nicht angegeben.

Querverweise

  • private, firstprivate, default, shared, copyinund reduction Klauseln (Abschnitt 2.7.2)
  • OMP_NUM_THREADS Umgebungsvariable
  • omp_set_dynamic-Bibliotheksfunktion
  • OMP_DYNAMIC Umgebungsvariable
  • omp_set_nested-Funktion
  • OMP_NESTED Umgebungsvariable
  • omp_set_num_threads-Bibliotheksfunktion

2.4 Arbeitsfreigabekonstrukte

Ein Arbeitsfreigabekonstrukt verteilt die Ausführung der zugeordneten Anweisung unter den Mitgliedern des Teams, auf die sie stoßen. Die Arbeitsfreigabedirektiven starten keine neuen Threads, und es gibt keine implizite Barriere für den Zugang zu einem Arbeitsfreigabekonstrukt.

Die Sequenz von Arbeitsfreigabekonstrukten und barrier Direktiven muss für jeden Thread in einem Team identisch sein.

OpenMP definiert die folgenden Arbeitsfreigabekonstrukte, und diese Konstrukte werden in den folgenden Abschnitten beschrieben:

2.4.1 für das Konstrukt

Die for Direktive identifiziert ein iteratives Arbeitsfreigabekonstrukt, das angibt, dass die Iterationen der zugeordneten Schleife parallel ausgeführt werden. Die Iterationen der for Schleife werden über Threads verteilt, die bereits im Team vorhanden sind und das parallele Konstrukt ausführen, an das sie gebunden wird. Die Syntax des for Konstrukts lautet wie folgt:

#pragma omp for [clause[[,] clause] ... ] new-line for-loop

Die Klausel ist eine der folgenden:

  • private(Variable-Liste)
  • firstprivate(Variable-Liste)
  • lastprivate(Variable-Liste)
  • reduction(Operatorvariablenliste:)
  • ordered
  • schedule(Art [,chunk_size] )
  • nowait

Die for Direktive legt Einschränkungen für die Struktur der entsprechenden for Schleife fest. Insbesondere muss die entsprechende for Schleife kanonische Form aufweisen:

for (init-expr;var logical-op b;incr-expr)

init-expr
Einer der folgenden:

  • var = lb
  • integer-type var = lb

incr-Ausdruck
Einer der folgenden:

  • ++var
  • var++
  • --var
  • var--
  • var+=incr
  • var-=incr
  • var var+=incr
  • var=incr+var
  • var var-=incr

var
Eine signierte ganzzahlige Variable. Wenn diese Variable andernfalls freigegeben werden würde, wird sie implizit für die Dauer der for. Ändern Sie diese Variable nicht im Textkörper der for Anweisung. Sofern die Variable nicht angegeben lastprivateist, ist der Wert nach der Schleife unbestimmt.

Logisches Op
Einer der folgenden:

  • <
  • <=
  • >
  • >=

lb, b und incr
Loop invariant integer expressions. Während der Auswertung dieser Ausdrücke gibt es keine Synchronisierung, sodass alle ausgewerteten Nebenwirkungen unbestimmte Ergebnisse erzeugen.

Das kanonische Formular ermöglicht die Anzahl der Schleifeniterationen, die beim Eintrag in die Schleife berechnet werden. Diese Berechnung wird nach integralen Werbeaktionen mit Werten im Var-Typ hergestellt. Wenn der Wert von b-lb+incr in diesem Typ nicht dargestellt werden kann, ist das Ergebnis unbestimmt. Wenn "logical-op" oder "incr-expr" vorhanden ist<, <=muss "var" bei jeder Iteration der Schleife erhöht werden. Wenn "logical-op" vorhanden ist > oder>=, muss "incr-expr" dazu führen, dass var bei jeder Iteration der Schleife kleiner wird.

Die schedule Klausel gibt an, wie Iterationen der for Schleife in Threads des Teams aufgeteilt werden. Die Richtigkeit eines Programms darf nicht davon abhängen, welcher Thread eine bestimmte Iteration ausführt. Der Wert von chunk_size muss, falls angegeben, ein invarianter ganzzahliger Schleifenausdruck mit einem positiven Wert sein. Während der Auswertung dieses Ausdrucks gibt es keine Synchronisierung, sodass alle ausgewerteten Nebenwirkungen unbestimmte Ergebnisse erzeugen. Die Art des Zeitplans kann einer der folgenden Werte sein:

Tabelle 2-1: schedule Klauselntypwerte

Wert Beschreibung
static Wenn schedule(static,chunk_size) angegeben wird, werden Iterationen in Blöcke einer durch chunk_size angegebenen Größe unterteilt. Die Blöcke werden in der Reihenfolge der Threadnummer statisch Threads im Team in einer Roundrobin-Weise zugewiesen. Wenn keine chunk_size angegeben wird, wird der Iterationsraum in Blöcke unterteilt, die ungefähr gleich groß sind, wobei jedem Thread ein Block zugewiesen ist.
dynamisch Wenn schedule(dynamic,chunk_size) angegeben wird, werden die Iterationen in eine Reihe von Blöcken unterteilt, die jeweils chunk_size Iterationen enthalten. Jeder Block wird einem Thread zugewiesen, der auf eine Zuordnung wartet. Der Thread führt den Teil der Iterationen aus und wartet dann auf die nächste Zuordnung, bis keine Blöcke erneut zugewiesen werden Standard. Der letzte zuzuweisende Teil kann eine kleinere Anzahl von Iterationen aufweisen. Wenn kein chunk_size angegeben wird, wird standardmäßig 1 festgelegt.
Geführte Wenn schedule(guided,chunk_size) angegeben wird, werden die Iterationen Threads in Blöcken mit abnehmenden Größen zugewiesen. Wenn ein Thread seinen zugewiesenen Teil der Iterationen beendet, wird es dynamisch einem anderen Block zugewiesen, bis keines übrig ist. Bei einem chunk_size von 1 ist die Größe jedes Blocks ungefähr die Anzahl der nicht zugewiesenen Iterationen dividiert durch die Anzahl der Threads. Diese Größen sinken fast exponentiell auf 1. Bei einem chunk_size mit wert k größer als 1 verringern sich die Größen fast exponentiell auf k, mit der Ausnahme, dass der letzte Block weniger als k Iterationen aufweisen kann. Wenn kein chunk_size angegeben wird, wird standardmäßig 1 festgelegt.
runtime Wenn schedule(runtime) angegeben, wird die Entscheidung zur Planung bis zur Laufzeit zurückgestellt. Der Zeitplantyp und die Größe der Blöcke können zur Laufzeit ausgewählt werden, indem die Umgebungsvariable OMP_SCHEDULEfestgelegt wird. Wenn diese Umgebungsvariable nicht festgelegt ist, wird der resultierende Zeitplan implementierungsdefiniert. Wenn schedule(runtime) angegeben, darf chunk_size nicht angegeben werden.

Wenn keine explizit definierte schedule Klausel vorhanden ist, wird die Standardimplementierung schedule definiert.

Ein OpenMP-kompatibles Programm sollte nicht auf einen bestimmten Zeitplan für die korrekte Ausführung angewiesen sein. Ein Programm sollte sich nicht auf eine Zeitplanart verlassen, die genau mit der oben angegebenen Beschreibung übereinstimmt, da es möglich ist, Variationen in den Implementierungen derselben Zeitplanart über verschiedene Compiler hinweg zu haben. Die Beschreibungen können verwendet werden, um den Zeitplan auszuwählen, der für eine bestimmte Situation geeignet ist.

Die ordered Klausel muss vorhanden sein, wenn ordered Direktiven an das for Konstrukt gebunden werden.

Am Ende eines for Konstrukts gibt es eine implizite Barriere, es sei denn, eine nowait Klausel ist angegeben.

Einschränkungen für die for Richtlinie lauten wie folgt:

  • Die for Schleife muss ein strukturierter Block sein, und darüber hinaus darf die Ausführung nicht durch eine break Anweisung beendet werden.

  • Die Werte der Schleifensteuerungsausdrücke der Schleife, die for einer for Direktive zugeordnet sind, müssen für alle Threads im Team identisch sein.

  • Die Iterationsvariable der for Schleife muss über einen signierten ganzzahligen Typ verfügen.

  • Nur eine einzelne schedule Klausel kann in einer for Direktive angezeigt werden.

  • Nur eine einzelne ordered Klausel kann in einer for Direktive angezeigt werden.

  • Nur eine einzelne nowait Klausel kann in einer for Direktive angezeigt werden.

  • Es ist nicht angegeben, ob oder wie oft Nebenwirkungen innerhalb der chunk_size, lb, b oder incr-Ausdrücke auftreten.

  • Der Wert des chunk_size Ausdrucks muss für alle Threads im Team identisch sein.

Querverweise

2.4.2 Abschnittskonstrukt

Die sections Direktive identifiziert ein nichtiteratives Arbeitsfreigabekonstrukt, das eine Reihe von Konstrukten angibt, die in Threads in einem Team aufgeteilt werden sollen. Jeder Abschnitt wird einmal von einem Thread im Team ausgeführt. Die Syntax der sections Direktive lautet wie folgt:

#pragma omp sections [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block ]
...
}

Die Klausel ist eine der folgenden:

  • private(Variable-Liste)
  • firstprivate(Variable-Liste)
  • lastprivate(Variable-Liste)
  • reduction(Operatorvariablenliste:)
  • nowait

Jedem Abschnitt wird eine section Direktive vorangestellt, obwohl die section Direktive für den ersten Abschnitt optional ist. Die section Richtlinien müssen im lexikalischen Umfang der sections Richtlinie erscheinen. Am Ende eines sections Konstrukts gibt es eine implizite Barriere, es sei denn, ein nowait Konstrukt ist angegeben.

Einschränkungen für die sections Richtlinie lauten wie folgt:

  • Eine section Richtlinie darf nicht außerhalb des lexikalischen Umfangs der sections Richtlinie erscheinen.

  • Nur eine einzelne nowait Klausel kann in einer sections Direktive angezeigt werden.

Querverweise

  • private, firstprivate, lastprivateund reduction Klauseln (Abschnitt 2.7.2)

2.4.3 Einzelkonstrukt

Die single Direktive identifiziert ein Konstrukt, das angibt, dass der zugeordnete strukturierte Block nur von einem Thread im Team ausgeführt wird (nicht unbedingt der Masterthread). Die Syntax der single Direktive lautet wie folgt:

#pragma omp single [clause[[,] clause] ...] new-linestructured-block

Die Klausel ist eine der folgenden:

  • private(Variable-Liste)
  • firstprivate(Variable-Liste)
  • copyprivate(Variable-Liste)
  • nowait

Nach dem single Konstrukt gibt es eine implizite Barriere, es sei denn, eine nowait Klausel ist angegeben.

Einschränkungen für die single Richtlinie lauten wie folgt:

  • Nur eine einzelne nowait Klausel kann in einer single Direktive angezeigt werden.
  • Die copyprivate Klausel darf nicht mit der nowait Klausel verwendet werden.

Querverweise

2.5 Kombinierte parallele Arbeitsfreigabekonstrukte

Kombinierte parallele Arbeitsfreigabekonstrukte sind Verknüpfungen zum Angeben einer parallelen Region, die nur ein Arbeitsfreigabekonstrukt aufweist. Die Semantik dieser Direktiven entspricht dem expliziten Angeben einer parallel Direktive gefolgt von einem einzelnen Arbeitsfreigabekonstrukt.

In den folgenden Abschnitten werden die kombinierten parallelen Arbeitsfreigabekonstrukte beschrieben:

2.5.1 parallel für das Konstrukt

Die parallel for Direktive ist eine Verknüpfung für eine parallel Region, die nur eine einzige for Direktive enthält. Die Syntax der parallel for Direktive lautet wie folgt:

#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop

Diese Richtlinie erlaubt alle Klauseln der parallel Richtlinie und der for Richtlinie, mit Ausnahme der nowait Klausel, mit identischen Bedeutungen und Einschränkungen. Die Semantik ist identisch mit der expliziten Angabe einer parallel Direktive unmittelbar gefolgt von einer for Direktive.

Querverweise

2.5.2 parallele Abschnitte konstruieren

Die parallel sections Direktive stellt ein Verknüpfungsformular zum Angeben eines parallel Bereichs bereit, der nur eine einzige sections Direktive aufweist. Die Semantik ist identisch mit der expliziten Angabe einer parallel Direktive unmittelbar gefolgt von einer sections Direktive. Die Syntax der parallel sections Direktive lautet wie folgt:

#pragma omp parallel sections  [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block  ]
   ...
}

Die Klausel kann eine der Klauseln sein, die von den parallel Richtlinien sections akzeptiert werden, mit Ausnahme der nowait Klausel.

Querverweise

2.6 Master- und Synchronisierungsdirektiven

In den folgenden Abschnitten wird beschrieben:

2.6.1 Masterkonstrukt

Die master Direktive identifiziert ein Konstrukt, das einen strukturierten Block angibt, der vom Masterthread des Teams ausgeführt wird. Die Syntax der master Direktive lautet wie folgt:

#pragma omp master new-linestructured-block

Andere Threads im Team führen den zugeordneten strukturierten Block nicht aus. Es gibt keine konkludente Barriere, entweder beim Betreten oder Verlassen des Master-Konstrukts.

2.6.2 kritisches Konstrukt

Die critical Direktive identifiziert ein Konstrukt, das die Ausführung des zugeordneten strukturierten Blocks auf einen einzelnen Thread gleichzeitig einschränkt. Die Syntax der critical Direktive lautet wie folgt:

#pragma omp critical [(name)]  new-linestructured-block

Ein optionaler Name kann verwendet werden, um den kritischen Bereich zu identifizieren. Bezeichner, die zum Identifizieren eines kritischen Bereichs verwendet werden, weisen eine externe Verknüpfung auf und befinden sich in einem Namensraum, der von Bezeichnungen, Tags, Mitgliedern und normalen Bezeichnern getrennt ist.

Ein Thread wartet am Anfang eines kritischen Bereichs, bis kein anderer Thread einen kritischen Bereich (an einer beliebigen Stelle im Programm) mit demselben Namen ausführt. Alle nicht benannten critical Direktiven werden dem gleichen nicht angegebenen Namen zugeordnet.

2.6.3 Barriererichtlinie

Die barrier Direktive synchronisiert alle Threads in einem Team. Bei auftreten, wartet jeder Thread im Team, bis alle anderen diesen Punkt erreicht haben. Die Syntax der barrier Direktive lautet wie folgt:

#pragma omp barrier new-line

Nachdem alle Threads im Team die Barriere gefunden haben, beginnt jeder Thread im Team mit der Ausführung der Anweisungen nach der Barrieredirektive parallel. Da die barrier Direktive keine C-Sprachausweisung als Teil ihrer Syntax aufweist, gibt es einige Einschränkungen für die Platzierung innerhalb eines Programms. Weitere Informationen zur formalen Grammatik finden Sie in Anhang C. Das folgende Beispiel veranschaulicht diese Einschränkungen.

/* ERROR - The barrier directive cannot be the immediate
*          substatement of an if statement
*/
if (x!=0)
   #pragma omp barrier
...

/* OK - The barrier directive is enclosed in a
*      compound statement.
*/
if (x!=0) {
   #pragma omp barrier
}

2.6.4 Atomkonstrukt

Die atomic Direktive stellt sicher, dass ein bestimmter Speicherspeicherort atomisch aktualisiert wird, anstatt ihn der Möglichkeit mehrerer gleichzeitiger Schreibthreads zuzuweisen. Die Syntax der atomic Direktive lautet wie folgt:

#pragma omp atomic new-lineexpression-stmt

Die Ausdrucksanweisung muss über eine der folgenden Formen verfügen:

  • x Binop-Ausdruck=
  • x++
  • ++x
  • x--
  • --x

In den vorherigen Ausdrücken:

  • x ist ein lvalue-Ausdruck mit Skalartyp.

  • Ausdruck ist ein Ausdruck mit skalaren Typ und verweist nicht auf das durch x festgelegte Objekt.

  • binop ist kein überladener Operator und ist einer von +, , *, -, /, &, ^, |, , , oder >><<.

Obwohl die Implementierung definiert ist, ob eine Implementierung alle atomic Direktiven durch Richtlinien ersetzt critical , die denselben eindeutigen Namen haben, erlaubt die atomic Richtlinie eine bessere Optimierung. Häufig stehen Hardwareanweisungen zur Verfügung, die das Atomupdate mit dem geringsten Aufwand ausführen können.

Nur die Last und der Speicher des objekts, das von x bezeichnet wird, sind atomisch; die Auswertung des Ausdrucks ist nicht atomisch. Um Rennbedingungen zu vermeiden, sollten alle Parallelaktualisierungen des Standorts mit der atomic Richtlinie geschützt werden, mit Ausnahme derjenigen, die als frei von Rennbedingungen bekannt sind.

Einschränkungen für die atomic Richtlinie lauten wie folgt:

  • Alle atomtechnischen Verweise auf den Speicherort x im gesamten Programm sind erforderlich, um einen kompatiblen Typ zu haben.

Beispiele

extern float a[], *p = a, b;
/* Protect against races among multiple updates. */
#pragma omp atomic
a[index[i]] += b;
/* Protect against races with updates through a. */
#pragma omp atomic
p[i] -= 1.0f;

extern union {int n; float x;} u;
/* ERROR - References through incompatible types. */
#pragma omp atomic
u.n++;
#pragma omp atomic
u.x -= 1.0f;

2.6.5 Flush-Richtlinie

Die flush Direktive , ob explizit oder implizit, gibt einen "threadübergreifenden" Sequenzpunkt an, an dem die Implementierung erforderlich ist, um sicherzustellen, dass alle Threads in einem Team eine konsistente Ansicht bestimmter Objekte (unten angegeben) im Arbeitsspeicher haben. Dies bedeutet, dass frühere Auswertungen von Ausdrücken, die auf diese Objekte verweisen, abgeschlossen sind und nachfolgende Auswertungen noch nicht begonnen haben. Beispielsweise müssen Compiler die Werte der Objekte aus registern in den Arbeitsspeicher wiederherstellen, und die Hardware muss möglicherweise Schreibpuffer in den Arbeitsspeicher leeren und die Werte der Objekte aus dem Speicher neu laden.

Die Syntax der flush Direktive lautet wie folgt:

#pragma omp flush [(variable-list)]  new-line

Wenn die Objekte, die eine Synchronisierung erfordern, von Variablen festgelegt werden können, können diese Variablen in der optionalen Variablenliste angegeben werden. Wenn ein Zeiger in der Variablenliste vorhanden ist, wird der Zeiger selbst geleert, nicht das Objekt, auf das sich der Zeiger bezieht.

Eine flush Direktive ohne Variable-Liste synchronisiert alle freigegebenen Objekte mit Ausnahme nicht zugänglicher Objekte mit automatischer Speicherdauer. (Dies hat wahrscheinlich mehr Aufwand als bei flush einer Variablenliste.) Eine flush Direktive ohne Variablenliste wird für die folgenden Direktiven impliziert:

  • barrier
  • Bei Eintritt und Ausgang von critical
  • Bei Eintritt und Ausgang von ordered
  • Bei Eintritt und Ausgang von parallel
  • Am Ausgang von for
  • Am Ausgang von sections
  • Am Ausgang von single
  • Bei Eintritt und Ausgang von parallel for
  • Bei Eintritt und Ausgang von parallel sections

Die Direktive wird nicht impliziert, wenn eine nowait Klausel vorhanden ist. Es ist darauf hinzuweisen, dass die flush Richtlinie für eine der folgenden Punkte nicht impliziert wird:

  • Bei Eintritt zu for
  • Bei Eintritt oder Ausgang von master
  • Bei Eintritt zu sections
  • Bei Eintritt zu single

Ein Verweis, der auf den Wert eines Objekts mit einem veränderlich qualifizierten Typ zugreift, verhält sich wie eine flush Direktive, die das Objekt am vorherigen Sequenzpunkt angibt. Ein Verweis, der den Wert eines Objekts mit einem veränderlich qualifizierten Typ ändert, verhält sich wie eine flush Direktive, die an dem nachfolgenden Sequenzpunkt angibt.

Da die flush Direktive keine C-Sprachausweisung als Teil ihrer Syntax aufweist, gibt es einige Einschränkungen für die Platzierung innerhalb eines Programms. Weitere Informationen zur formalen Grammatik finden Sie in Anhang C. Das folgende Beispiel veranschaulicht diese Einschränkungen.

/* ERROR - The flush directive cannot be the immediate
*          substatement of an if statement.
*/
if (x!=0)
   #pragma omp flush (x)
...

/* OK - The flush directive is enclosed in a
*      compound statement
*/
if (x!=0) {
   #pragma omp flush (x)
}

Einschränkungen für die flush Richtlinie lauten wie folgt:

  • Eine in einer flush Direktive angegebene Variable darf keinen Verweistyp aufweisen.

2.6.6 bestelltes Konstrukt

Der strukturierte Block nach einer ordered Direktive wird in der Reihenfolge ausgeführt, in der Iterationen in einer sequenziellen Schleife ausgeführt werden. Die Syntax der ordered Direktive lautet wie folgt:

#pragma omp ordered new-linestructured-block

Eine ordered Richtlinie muss innerhalb des dynamischen Umfangs eines oder parallel for eines for Konstrukts sein. Die for Oder parallel for Direktive, an die das ordered Konstrukt gebunden wird, muss eine ordered Klausel aufweisen, wie in Abschnitt 2.4.1 beschrieben. Bei der Ausführung eines for oder parallel for Eines Konstrukts mit einer ordered Klausel werden Konstrukte streng in der Reihenfolge ausgeführt, ordered in der sie in einer sequenziellen Ausführung der Schleife ausgeführt werden.

Einschränkungen für die ordered Richtlinie lauten wie folgt:

  • Eine Iteration einer Schleife mit einem for Konstrukt darf nicht mehr als einmal dieselbe geordnete Direktive ausführen, und sie darf nicht mehr als eine ordered Direktive ausführen.

2.7 Datenumgebung

In diesem Abschnitt wird eine Direktive und mehrere Klauseln zum Steuern der Datenumgebung während der Ausführung paralleler Regionen wie folgt dargestellt:

  • Eine Threadprivate-Direktive wird bereitgestellt, um Dateibereichs-, Namespacebereichs- oder statische Blockbereichsvariablen lokal in einem Thread zu erstellen.

  • Klauseln, die für die Direktiven angegeben werden können, um die Freigabeattribute von Variablen für die Dauer der parallelen oder Arbeitsfreigabekonstrukte zu steuern, werden in Abschnitt 2.7.2 beschrieben.

2.7.1 threadprivate-Direktive

Die threadprivate Direktive macht die benannten Dateibereichs-, Namespace-Bereichs- oder statischen Blockbereichsvariablen in der Variablenliste privat in einem Thread. Variablenliste ist eine durch Trennzeichen getrennte Liste von Variablen, die keinen unvollständigen Typ aufweisen. Die Syntax der threadprivate Direktive lautet wie folgt:

#pragma omp threadprivate(variable-list) new-line

Jede Kopie einer threadprivate Variablen wird einmal initialisiert, an einem nicht angegebenen Punkt im Programm vor dem ersten Verweis auf diese Kopie und in der üblichen Weise (d. h., wenn die Masterkopie in einer seriellen Ausführung des Programms initialisiert wird). Wenn auf ein Objekt in einem expliziten Initialisierer einer threadprivate Variablen verwiesen wird und der Wert des Objekts vor dem ersten Verweis auf eine Kopie der Variablen geändert wird, ist das Verhalten nicht angegeben.

Wie bei jeder privaten Variable darf ein Thread nicht auf die Kopie eines Objekts eines threadprivate anderen Threads verweisen. Während serieller Bereiche und Masterbereiche des Programms werden Verweise auf die Kopie des Masterthreads des Objekts verwendet.

Nachdem der erste parallele Bereich ausgeführt wurde, werden die Daten in den threadprivate Objekten garantiert nur beibehalten, wenn der dynamische Standard Threads-Mechanismus deaktiviert wurde und die Anzahl der Threads für alle parallelen Regionen unverändert ist.

Die Einschränkungen für die threadprivate Richtlinie lauten wie folgt:

  • Eine threadprivate Direktive für Dateibereichs- oder Namespacebereichsvariablen muss außerhalb einer Definition oder Deklaration angezeigt werden und muss lexikalisch allen Verweisen auf eine der Variablen in der Liste vorangestellt werden.

  • Jede Variable in der Variablenliste einer threadprivate Direktive im Datei- oder Namespacebereich muss auf eine Variabledeklaration im Datei- oder Namespacebereich verweisen, die lexikalisch vor der Direktive steht.

  • Eine threadprivate Direktive für statische Blockbereichsvariablen muss im Bereich der Variablen und nicht in einem geschachtelten Bereich angezeigt werden. Die Richtlinie muss lexikalisch allen Verweisen auf eine der Variablen in ihrer Liste vorangehen.

  • Jede Variable in der Variablenliste einer threadprivate Direktive im Blockbereich muss auf eine Variabledeklaration im selben Bereich verweisen, der lexikalisch vor der Direktive steht. Die Variabledeklaration muss den statischen Speicherklassenbezeichner verwenden.

  • Wenn eine Variable in einer threadprivate Direktive in einer Übersetzungseinheit angegeben ist, muss sie in einer threadprivate Direktive in jeder Übersetzungseinheit angegeben werden, in der sie deklariert wird.

  • Eine threadprivate Variable darf nicht in einer Klausel mit Ausnahme der copyinKlausel , copyprivate, , schedulenum_threads, oder der if Klausel angezeigt werden.

  • Die Adresse einer threadprivate Variablen ist keine Adresskonstante.

  • Eine threadprivate Variable darf keinen unvollständigen Typ oder einen Verweistyp aufweisen.

  • Eine threadprivate Variable mit nicht-POD-Klassentyp muss über einen barrierefreien, eindeutigen Kopierkonstruktor verfügen, wenn sie mit einem expliziten Initialisierer deklariert wird.

Im folgenden Beispiel wird veranschaulicht, wie das Ändern einer Variablen, die in einem Initialisierungsprogramm angezeigt wird, zu einem nicht angegebenen Verhalten führen kann und wie Sie dieses Problem auch mithilfe eines Hilfsobjekts und eines Kopierkonstruktors vermeiden können.

int x = 1;
T a(x);
const T b_aux(x); /* Capture value of x = 1 */
T b(b_aux);
#pragma omp threadprivate(a, b)

void f(int n) {
   x++;
   #pragma omp parallel for
   /* In each thread:
   * Object a is constructed from x (with value 1 or 2?)
   * Object b is copy-constructed from b_aux
   */
   for (int i=0; i<n; i++) {
      g(a, b); /* Value of a is unspecified. */
   }
}

Querverweise

2.7.2 Datenfreigabe-Attributklauseln

Mehrere Direktiven akzeptieren Klauseln, mit denen ein Benutzer die Freigabeattribute von Variablen für die Dauer des Bereichs steuern kann. Freigabe-Attributklauseln gelten nur für Variablen im lexikalischen Umfang der Direktive, für die die Klausel angezeigt wird. Nicht alle folgenden Klauseln sind für alle Direktiven zulässig. Die Liste der Klauseln, die für eine bestimmte Richtlinie gültig sind, werden mit der Richtlinie beschrieben.

Wenn eine Variable sichtbar ist, wenn ein paralleles Konstrukt oder ein Arbeitsfreigabekonstrukt gefunden wird und die Variable in einer Freigabe-Attributklausel oder threadprivate -direktive nicht angegeben ist, wird die Variable freigegeben. Statische Variablen, die innerhalb des dynamischen Umfangs eines parallelen Bereichs deklariert werden, werden gemeinsam genutzt. Heap zugewiesener Speicher (z. B. mit malloc() C oder C++ oder dem new Operator in C++) wird freigegeben. (Der Zeiger auf diesen Speicher kann jedoch privat oder freigegeben sein.) Variablen mit automatischer Speicherdauer, die innerhalb des dynamischen Umfangs eines parallelen Bereichs deklariert ist, sind privat.

Die meisten Klauseln akzeptieren ein Argument mit variabler Liste , bei dem es sich um eine durch Trennzeichen getrennte Liste von Variablen handelt, die sichtbar sind. Wenn eine Variable, auf die in einer Datenfreigabe-Attributklausel verwiesen wird, einen von einer Vorlage abgeleiteten Typ aufweist und keine anderen Verweise auf diese Variable im Programm vorhanden sind, ist das Verhalten nicht definiert.

Alle Variablen, die in Direktivenklauseln angezeigt werden, müssen sichtbar sein. Klauseln können nach Bedarf wiederholt werden, aber in mehreren Klauseln kann keine Variable angegeben werden, außer dass eine Variable sowohl in einer firstprivate als auch in einer lastprivate Klausel angegeben werden kann.

In den folgenden Abschnitten werden die Datenfreigabe-Attributklauseln beschrieben:

2.7.2.1 private

Die private Klausel deklariert die Variablen in variabler Liste als privat für jeden Thread in einem Team. Die Syntax der private Klausel lautet wie folgt:

private(variable-list)

Das Verhalten einer variablen, die in einer private Klausel angegeben ist, lautet wie folgt. Für das Konstrukt wird ein neues Objekt mit automatischer Speicherdauer zugewiesen. Die Größe und Ausrichtung des neuen Objekts werden durch den Typ der Variablen bestimmt. Diese Zuordnung erfolgt einmal für jeden Thread im Team, und bei Bedarf wird ein Standardkonstruktor für ein Klassenobjekt aufgerufen. andernfalls ist der Anfangswert unbestimmt. Das ursprüngliche Objekt, auf das von der Variablen verwiesen wird, weist beim Eingeben des Konstrukts einen unbestimmten Wert auf, darf nicht innerhalb des dynamischen Umfangs des Konstrukts geändert werden und weist beim Verlassen des Konstrukts einen unbestimmten Wert auf.

Im lexikalischen Umfang des Direktivenkonstrukts verweist die Variable auf das neue private Objekt, das dem Thread zugeordnet ist.

Die Einschränkungen für die private Klausel lauten wie folgt:

  • Eine Variable mit einem Klassentyp, der in einer private Klausel angegeben ist, muss über einen barrierefreien, eindeutigen Standardkonstruktor verfügen.

  • Eine in einer private Klausel angegebene Variable darf keinen -qualifizierten Typ aufweisen const, es sei denn, sie verfügt über einen Klassentyp mit einem mutable Element.

  • Eine in einer private Klausel angegebene Variable darf keinen unvollständigen Typ oder einen Verweistyp aufweisen.

  • Variablen, die in der reduction Klausel einer parallel Direktive angezeigt werden, können nicht in einer private Klausel für eine Arbeitsfreigabedirektive angegeben werden, die an das parallele Konstrukt gebunden ist.

2.7.2.2 firstprivate

Die firstprivate Klausel stellt eine Obermenge der Funktionalität bereit, die von der private Klausel bereitgestellt wird. Die Syntax der firstprivate Klausel lautet wie folgt:

firstprivate(variable-list)

Variablen, die in der Variablenliste angegeben sind, weisen private die Klauselsemantik auf, wie in Abschnitt 2.7.2.1 beschrieben. Die Initialisierung oder Konstruktion erfolgt so, als ob sie einmal pro Thread durchgeführt wurde, bevor der Thread ausgeführt wird. Bei einer Klausel für ein firstprivate paralleles Konstrukt ist der Anfangswert des neuen privaten Objekts der Wert des ursprünglichen Objekts, das unmittelbar vor dem parallelen Konstrukt für den Thread vorhanden ist, der darauf stößt. Bei einer Klausel für ein firstprivate Arbeitsfreigabekonstrukt ist der Anfangswert des neuen privaten Objekts für jeden Thread, der das Arbeitsfreigabekonstrukt ausführt, der Wert des ursprünglichen Objekts, das vor dem Zeitpunkt vorhanden ist, zu dem derselbe Thread auf das Arbeitsfreigabekonstrukt trifft. Darüber hinaus wird für C++-Objekte das neue private Objekt für jeden Thread aus dem ursprünglichen Objekt erstellt.

Die Einschränkungen für die firstprivate Klausel lauten wie folgt:

  • Eine in einer firstprivate Klausel angegebene Variable darf keinen unvollständigen Typ oder einen Verweistyp aufweisen.

  • Eine Variable mit einem Klassentyp, der angegeben ist, muss firstprivate über einen barrierefreien, eindeutigen Kopierkonstruktor verfügen.

  • Variablen, die innerhalb eines parallelen Bereichs privat sind oder in der reduction Klausel einer parallel Direktive angezeigt werden, können nicht in einer firstprivate Klausel für eine Arbeitsfreigabedirektive angegeben werden, die an das parallele Konstrukt gebunden ist.

2.7.2.3 lastprivate

Die lastprivate Klausel stellt eine Obermenge der Funktionalität bereit, die von der private Klausel bereitgestellt wird. Die Syntax der lastprivate Klausel lautet wie folgt:

lastprivate(variable-list)

Variablen, die in der Variablenliste angegeben sind, weisen private Die Klauselsemantik auf. Wenn eine lastprivate Klausel in der Direktive angezeigt wird, die ein Arbeitsfreigabekonstrukt identifiziert, wird der Wert jeder lastprivate Variablen aus der sequenziellen letzten Iteration der zugeordneten Schleife oder der lexikalisch letzten Abschnittsdirektive dem ursprünglichen Objekt der Variablen zugewiesen. Variablen, denen nach der letzten Iteration des for oder parallel foroder des lexikalischen letzten Abschnitts oder sectionsparallel sections der Direktive kein Wert zugewiesen wird, weisen unbestimmte Werte nach dem Konstrukt auf. Nicht zugewiesene Unterobjekte weisen auch einen unbestimmten Wert nach dem Konstrukt auf.

Die Einschränkungen für die lastprivate Klausel lauten wie folgt:

  • Alle Einschränkungen gelten.private

  • Eine Variable mit einem Klassentyp, der angegeben ist, lastprivate muss über einen barrierefreien, eindeutigen Kopierzuweisungsoperator verfügen.

  • Variablen, die innerhalb eines parallelen Bereichs privat sind oder in der reduction Klausel einer parallel Direktive angezeigt werden, können nicht in einer lastprivate Klausel für eine Arbeitsfreigabedirektive angegeben werden, die an das parallele Konstrukt gebunden ist.

2.7.2.4 shared

Diese Klausel gibt Variablen frei, die in der Variablenliste für alle Threads in einem Team angezeigt werden. Alle Threads innerhalb eines Teams greifen auf denselben Speicherbereich für shared Variablen zu.

Die Syntax der shared Klausel lautet wie folgt:

shared(variable-list)

2.7.2.5 default

Die default Klausel ermöglicht es dem Benutzer, die Datenfreigabeattribute von Variablen zu beeinflussen. Die Syntax der default Klausel lautet wie folgt:

default(shared | none)

Die Angabe default(shared) entspricht der expliziten Auflistung jeder aktuell sichtbaren Variable in einer shared Klausel, es sei denn, sie ist threadprivate oder const-qualifiziert. Wenn keine explizite default Klausel vorhanden ist, ist das Standardverhalten identisch mit der default(shared) Angabe.

Die Angabe default(none) erfordert, dass mindestens einer der folgenden Werte für jeden Verweis auf eine Variable im lexikalischen Umfang des parallelen Konstrukts wahr sein muss:

  • Die Variable wird explizit in einer Datenfreigabe-Attributklausel eines Konstrukts aufgeführt, das den Verweis enthält.

  • Die Variable wird innerhalb des parallelen Konstrukts deklariert.

  • Die Variable ist threadprivate.

  • Die Variable weist einen const-qualifizierten Typ auf.

  • Die Variable ist die Schleifensteuerungsvariable für eine for Schleife, die unmittelbar auf eine for Schleife folgt, parallel for und der Variableverweis wird innerhalb der Schleife angezeigt.

Wenn Sie eine Variable für eine firstprivateoder lastprivateeine Klausel einer eingeschlossenen Direktive angeben, reduction wird ein impliziter Verweis auf die Variable im eingeschlossenen Kontext verursacht. Solche impliziten Verweise unterliegen auch den oben aufgeführten Anforderungen.

Nur eine einzelne default Klausel kann für eine parallel Richtlinie angegeben werden.

Das Standard-Datenfreigabe-Attribut einer Variablen kann mithilfe der privatereductionfirstprivatelastprivateshared folgenden Beispiele überschrieben werden:

#pragma  omp  parallel  for  default(shared)  firstprivate(i)\
   private(x)  private(r)  lastprivate(i)

2.7.2.6 reduction

Diese Klausel führt eine Reduzierung der Skalarvariablen durch, die in variabler Liste mit dem Operator op angezeigt werden. Die Syntax der reduction Klausel lautet wie folgt:

reduction(op:variable-list)

Eine Reduzierung wird in der Regel für eine Anweisung mit einem der folgenden Formen angegeben:

  • x x=op Ausdruck
  • xBinop-Ausdruck=
  • x Ausdruckx= (mit Ausnahme von Subtraktion)
  • x++
  • ++x
  • x--
  • --x

Dabei gilt Folgendes:

x
Eine der in der Liste angegebenen Reduktionsvariablen.

Variable-Liste
Eine durch Trennzeichen getrennte Liste von Skalarreduktionsvariablen.

expr
Ein Ausdruck mit Skalartyp, der nicht auf x verweist.

op
Kein überladener Operator, sondern einer von +, , , -, &, , , ^, |, , oder ||&&. *

binop
Kein überladener Operator, sondern einer von +, , , -, , &, , oder ^|. *

Im Folgenden sehen Sie ein Beispiel für die reduction Klausel:

#pragma omp parallel for reduction(+: a, y) reduction(||: am)
for (i=0; i<n; i++) {
   a += b[i];
   y = sum(y, c[i]);
   am = am || b[i] == c[i];
}

Wie im Beispiel gezeigt, kann ein Operator innerhalb eines Funktionsaufrufs ausgeblendet werden. Der Benutzer sollte vorsichtig sein, dass der in der reduction Klausel angegebene Operator mit dem Reduzierungsvorgang übereinstimmt.

Obwohl der rechte Operand des || Operators in diesem Beispiel keine Nebenwirkungen hat, sind sie zulässig, sollten aber mit Bedacht verwendet werden. In diesem Zusammenhang kann ein Nebeneffekt, der während der sequenziellen Ausführung der Schleife nicht auftreten kann, während der parallelen Ausführung auftreten. Dieser Unterschied kann auftreten, da die Reihenfolge der Ausführung der Iterationen unbestimmt ist.

Der Operator wird verwendet, um den Anfangswert aller privaten Variablen zu bestimmen, die vom Compiler für die Reduzierung verwendet werden, und um den Finalisierungsoperator zu bestimmen. Wenn Sie den Operator explizit angeben, kann die Reduktionsanweisung außerhalb des lexikalischen Umfangs des Konstrukts sein. Eine beliebige Anzahl von reduction Klauseln kann für die Richtlinie angegeben werden, eine Variable kann jedoch höchstens in einer reduction Klausel für diese Richtlinie angezeigt werden.

Es wird eine private Kopie jeder Variablen in der Variablenliste erstellt, eine für jeden Thread, als ob die private Klausel verwendet wurde. Die private Kopie wird entsprechend dem Operator initialisiert (siehe die folgende Tabelle).

Am Ende des Bereichs, für den die reduction Klausel angegeben wurde, wird das ursprüngliche Objekt aktualisiert, um das Ergebnis der Kombination des ursprünglichen Werts mit dem Endwert der einzelnen privaten Kopien mithilfe des angegebenen Operators widerzuspiegeln. Die Reduktionsoperatoren sind alle assoziativ (mit Ausnahme von Subtraktion), und der Compiler kann die Berechnung des endgültigen Werts frei neu zuordnen. (Die Teilergebnisse einer Subtraktionsreduktion werden hinzugefügt, um den Endwert zu bilden.)

Der Wert des ursprünglichen Objekts wird unbestimmt, wenn der erste Thread die enthaltende Klausel erreicht und erneut Standard so lange, bis die Berechnung der Reduzierung abgeschlossen ist. Normalerweise wird die Berechnung am Ende des Konstrukts abgeschlossen sein; Wenn die reduction Klausel jedoch für ein Konstrukt verwendet wird, auf das nowait auch angewendet wird, wird der Wert des ursprünglichen Objekts neu Standard unbestimmt, bis eine Synchronisierung einer Barriere durchgeführt wurde, um sicherzustellen, dass alle Threads die reduction Klausel abgeschlossen haben.

In der folgenden Tabelle sind die operatoren aufgeführt, die gültig sind und deren kanonische Initialisierungswerte sind. Der tatsächliche Initialisierungswert ist mit dem Datentyp der Reduzierungsvariablen konsistent.

Operator Initialisierung
+ 0
* 1
- 0
& ~0
| 0
^ 0
&& 1
|| 0

Die Einschränkungen für die reduction Klausel lauten wie folgt:

  • Der Typ der Variablen in der reduction Klausel muss für den Reduktionsoperator gültig sein, mit der Ausnahme, dass Zeigertypen und Verweistypen niemals zulässig sind.

  • Eine variable, die in der reduction Klausel angegeben ist, darf nicht -qualified sein const.

  • Variablen, die innerhalb eines parallelen Bereichs privat sind oder in der reduction Klausel einer parallel Direktive angezeigt werden, können nicht in einer reduction Klausel für eine Arbeitsfreigabedirektive angegeben werden, die an das parallele Konstrukt gebunden ist.

    #pragma omp parallel private(y)
    { /* ERROR - private variable y cannot be specified
                  in a reduction clause */
        #pragma omp for reduction(+: y)
        for (i=0; i<n; i++)
           y += b[i];
    }
    
    /* ERROR - variable x cannot be specified in both
                a shared and a reduction clause */
    #pragma omp parallel for shared(x) reduction(+: x)
    

2.7.2.7 copyin

Die copyin Klausel bietet einen Mechanismus zum Zuweisen desselben Werts zu threadprivate Variablen für jeden Thread im Team, der den parallelen Bereich ausführt. Für jede variable, die in einer copyin Klausel angegeben ist, wird der Wert der Variablen im Masterthread des Teams so kopiert, als ob dies durch die Zuweisung erfolgt, in die thread-privaten Kopien am Anfang des parallelen Bereichs. Die Syntax der copyin Klausel lautet wie folgt:

copyin(
variable-list
)

Die Einschränkungen für die copyin Klausel lauten wie folgt:

  • Eine Variable, die in der copyin Klausel angegeben ist, muss über einen barrierefreien, eindeutigen Kopierzuweisungsoperator verfügen.

  • Eine Variable, die in der copyin Klausel angegeben ist, muss eine threadprivate Variable sein.

2.7.2.8 copyprivate

Die copyprivate Klausel stellt einen Mechanismus bereit, mit dem eine private Variable einen Wert von einem Mitglied eines Teams an die anderen Mitglieder übertragen kann. Es ist eine Alternative zur Verwendung einer freigegebenen Variablen für den Wert, wenn eine solche freigegebene Variable bereitgestellt wird, schwierig wäre (z. B. in einer Rekursion, die eine andere Variable auf jeder Ebene erfordert). Die copyprivate Klausel kann nur in der single Direktive angezeigt werden.

Die Syntax der copyprivate Klausel lautet wie folgt:

copyprivate(
variable-list
)

Die Auswirkung der copyprivate Klausel auf die Variablen in der Variablenliste tritt nach der Ausführung des dem Konstrukt zugeordneten single strukturierten Blocks auf, und bevor eines der Threads im Team die Barriere am Ende des Konstrukts verlassen hat. Anschließend wird diese Variable in allen anderen Threads im Team für jede Variable in der Variablenliste definiert (wie durch Zuweisung), mit dem Wert der entsprechenden Variablen im Thread, der den strukturierten Block des Konstrukts ausgeführt hat.

Einschränkungen für die copyprivate Klausel lauten wie folgt:

  • Eine Variable, die in der copyprivate Klausel angegeben ist, darf nicht in einer private oder firstprivate einer Klausel für dieselbe single Direktive angezeigt werden.

  • Wenn eine single Direktive mit einer copyprivate Klausel im dynamischen Umfang eines parallelen Bereichs auftritt, müssen alle in der copyprivate Klausel angegebenen Variablen im eingeschlossenen Kontext privat sein.

  • Eine Variable, die in der copyprivate Klausel angegeben ist, muss über einen eindeutigen Zuweisungsoperator für eindeutige Kopien verfügen.

2.8 Bindende Richtlinie

Dynamische Bindung von Direktiven muss den folgenden Regeln entsprechen:

  • Die for, sections, single, , masterund barrier Direktiven binden an die dynamisch eingeschlossenen parallel, falls vorhanden, unabhängig vom Wert einer Klausel if , die in dieser Richtlinie vorhanden sein kann. Wenn derzeit keine parallele Region ausgeführt wird, werden die Direktiven von einem Team ausgeführt, das nur aus dem Masterthread besteht.

  • Die ordered Direktive wird an die dynamisch umschließende forDirektive gebunden.

  • Die atomic Direktive erzwingt exklusiven Zugriff in Bezug auf atomic Direktiven in allen Threads, nicht nur im aktuellen Team.

  • Die critical Direktive erzwingt exklusiven Zugriff in Bezug auf critical Direktiven in allen Threads, nicht nur im aktuellen Team.

  • Eine Direktive kann niemals an eine Direktive gebunden werden, die sich außerhalb der am ehesten dynamisch umschließenden parallel.

2.9 Schachtelung der Richtlinie

Die dynamische Schachtelung von Direktiven muss den folgenden Regeln entsprechen:

  • Eine parallel Direktive innerhalb einer anderen parallel logischen Legt ein neues Team her, das nur aus dem aktuellen Thread besteht, es sei denn, geschachtelte Parallelität ist aktiviert.

  • for, sectionsund single Direktiven, die sich an dasselbe parallel binden, dürfen nicht miteinander verschachtelt werden.

  • critical Direktiven mit demselben Namen dürfen nicht miteinander verschachtelt werden. Beachten Sie, dass diese Einschränkung nicht ausreicht, um deadlock zu verhindern.

  • for, sectionsund single Richtlinien sind im dynamischen Umfang von critical, orderedund master Regionen nicht zulässig, wenn die Richtlinien an die gleichen parallel wie die Regionen gebunden sind.

  • barrier Direktiven sind im dynamischen Umfang von for, , ordered, sections, single, masterund critical Regionen nicht zulässig, wenn die Direktiven an die gleichen parallel Wie die Regionen gebunden sind.

  • master Direktiven sind im dynamischen Umfang von for, sectionsund single Direktiven nicht zulässig, wenn die master Richtlinien an die gleichen wie parallel die Arbeitsfreigabedirektiven gebunden sind.

  • ordered Direktiven sind im dynamischen Umfang von critical Regionen nicht zulässig, wenn die Direktiven an die gleichen parallel Wie die Regionen gebunden sind.

  • Jede Direktive, die bei dynamischer Ausführung innerhalb eines parallelen Bereichs zulässig ist, ist auch zulässig, wenn sie außerhalb eines parallelen Bereichs ausgeführt wird. Wenn sie dynamisch außerhalb eines vom Benutzer angegebenen parallelen Bereichs ausgeführt wird, wird die Direktive von einem Team ausgeführt, das nur aus dem Masterthread besteht.