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:
Wenn die
num_threads
Klausel vorhanden ist, ist der Wert des ganzzahligen Ausdrucks die Anzahl der angeforderten Threads.Wenn die
omp_set_num_threads
Bibliotheksfunktion aufgerufen wurde, ist der Wert des Arguments im zuletzt ausgeführten Aufruf die Anzahl der angeforderten Threads.Wenn die Umgebungsvariable
OMP_NUM_THREADS
definiert ist, ist der Wert dieser Umgebungsvariable die Anzahl der angeforderten Threads.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_NESTED
geä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. Dernum_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 istnum_threads
nicht angegeben.
Querverweise
private
,firstprivate
,default
,shared
,copyin
undreduction
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:
- für direktive
- Abschnittsdirektive
- Einzeldirektive
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 lastprivate
ist, 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_SCHEDULE festgelegt 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 einebreak
Anweisung beendet werden.Die Werte der Schleifensteuerungsausdrücke der Schleife, die
for
einerfor
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 einerfor
Direktive angezeigt werden.Nur eine einzelne
ordered
Klausel kann in einerfor
Direktive angezeigt werden.Nur eine einzelne
nowait
Klausel kann in einerfor
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
private
,firstprivate
,lastprivate
undreduction
Klauseln (Abschnitt 2.7.2)- OMP_SCHEDULE Umgebungsvariable
- geordnetes Konstrukt
- Schedule-Klausel
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 dersections
Richtlinie erscheinen.Nur eine einzelne
nowait
Klausel kann in einersections
Direktive angezeigt werden.
Querverweise
private
,firstprivate
,lastprivate
undreduction
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 einersingle
Direktive angezeigt werden. - Die
copyprivate
Klausel darf nicht mit dernowait
Klausel verwendet werden.
Querverweise
private
,firstprivate
undcopyprivate
Klauseln (Abschnitt 2.7.2)
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:
- Parallel zur Direktive
- Parallele Abschnittsdirektive
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
- Parallele Direktive
- für direktive
- Daten-Attributklauseln
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
- Parallele Direktive
- Abschnittsdirektive
2.6 Master- und Synchronisierungsdirektiven
In den folgenden Abschnitten wird beschrieben:
- Master-Konstrukt
- Kritisches Konstrukt
- Barrieredirektive
- Atomkonstrukt
- Flush-Direktive
- geordnetes Konstrukt
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 eineordered
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 einerthreadprivate
Direktive in jeder Übersetzungseinheit angegeben werden, in der sie deklariert wird.Eine
threadprivate
Variable darf nicht in einer Klausel mit Ausnahme dercopyin
Klausel ,copyprivate
, ,schedule
num_threads
, oder derif
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
- Dynamische Threads
- OMP_DYNAMIC Umgebungsvariable
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 aufweisenconst
, es sei denn, sie verfügt über einen Klassentyp mit einemmutable
Element.Eine in einer
private
Klausel angegebene Variable darf keinen unvollständigen Typ oder einen Verweistyp aufweisen.Variablen, die in der
reduction
Klausel einerparallel
Direktive angezeigt werden, können nicht in einerprivate
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 einerparallel
Direktive angezeigt werden, können nicht in einerfirstprivate
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 for
oder des lexikalischen letzten Abschnitts oder sections
parallel 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 einerparallel
Direktive angezeigt werden, können nicht in einerlastprivate
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 einefor
Schleife folgt,parallel for
und der Variableverweis wird innerhalb der Schleife angezeigt.
Wenn Sie eine Variable für eine firstprivate
oder lastprivate
eine 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 private
reduction
firstprivate
lastprivate
shared
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 seinconst
.Variablen, die innerhalb eines parallelen Bereichs privat sind oder in der
reduction
Klausel einerparallel
Direktive angezeigt werden, können nicht in einerreduction
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 einethreadprivate
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 einerprivate
oderfirstprivate
einer Klausel für dieselbesingle
Direktive angezeigt werden.Wenn eine
single
Direktive mit einercopyprivate
Klausel im dynamischen Umfang eines parallelen Bereichs auftritt, müssen alle in dercopyprivate
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
, ,master
undbarrier
Direktiven binden an die dynamisch eingeschlossenenparallel
, falls vorhanden, unabhängig vom Wert einer Klauselif
, 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ßendefor
Direktive gebunden.Die
atomic
Direktive erzwingt exklusiven Zugriff in Bezug aufatomic
Direktiven in allen Threads, nicht nur im aktuellen Team.Die
critical
Direktive erzwingt exklusiven Zugriff in Bezug aufcritical
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 anderenparallel
logischen Legt ein neues Team her, das nur aus dem aktuellen Thread besteht, es sei denn, geschachtelte Parallelität ist aktiviert.for
,sections
undsingle
Direktiven, die sich an dasselbeparallel
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
,sections
undsingle
Richtlinien sind im dynamischen Umfang voncritical
,ordered
undmaster
Regionen nicht zulässig, wenn die Richtlinien an die gleichenparallel
wie die Regionen gebunden sind.barrier
Direktiven sind im dynamischen Umfang vonfor
, ,ordered
,sections
,single
,master
undcritical
Regionen nicht zulässig, wenn die Direktiven an die gleichenparallel
Wie die Regionen gebunden sind.master
Direktiven sind im dynamischen Umfang vonfor
,sections
undsingle
Direktiven nicht zulässig, wenn diemaster
Richtlinien an die gleichen wieparallel
die Arbeitsfreigabedirektiven gebunden sind.ordered
Direktiven sind im dynamischen Umfang voncritical
Regionen nicht zulässig, wenn die Direktiven an die gleichenparallel
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.
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für