Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Direktiven baseras på #pragma direktiv som definieras i C- och C++-standarderna. Kompilatorer som stöder OpenMP C- och C++-API:et innehåller ett kommandoradsalternativ som aktiverar och tillåter tolkning av alla OpenMP-kompilatordirektiv.
2.1 Direktivformat
Syntaxen för ett OpenMP-direktiv anges formellt av grammatiken i bilaga C och informellt enligt följande:
#pragma omp directive-name [clause[ [,] clause]...] new-line
Varje direktiv börjar med #pragma omp, för att minska risken för konflikt med andra (icke-OpenMP- eller leverantörstillägg till OpenMP) pragma-direktiv med samma namn. Resten av direktivet följer konventionerna i C- och C++-standarderna för kompilatordirektiv. I synnerhet kan blanksteg användas före och efter #, och ibland måste blanksteg användas för att avgränsa orden i ett direktiv. Tokens för förbearbetning som följer #pragma omp är föremål för makroersättning.
Direktiven är skiftlägeskänsliga. Ordningen i vilken klausuler visas i direktiv är inte betydande. Klausuler om direktiv kan upprepas efter behov, med förbehåll för de begränsningar som anges i beskrivningen av varje klausul. Om variabellistan visas i en sats måste den endast ange variabler. Endast ett direktivnamn kan anges per direktiv. Följande direktiv är till exempel inte tillåtet:
/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier
Ett OpenMP-direktiv gäller för högst en efterföljande instruktion, som måste vara ett strukturerat block.
2.2 Villkorsstyrd kompilering
Makronamnet _OPENMP definieras av OpenMP-kompatibla implementeringar som decimalkonstanten yyyymm, vilket blir året och månaden för den godkända specifikationen. Det här makrot får inte omfattas av ett #define eller ett #undef förbearbetningsdirektiv.
#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif
Om leverantörer definierar tillägg till OpenMP kan de ange ytterligare fördefinierade makron.
2.3 parallellkonstruktion
Följande direktiv definierar en parallell region, som är en region i programmet som ska köras av många trådar parallellt. Detta direktiv är den grundläggande konstruktion som startar parallell körning.
#pragma omp parallel [clause[ [, ]clause] ...] new-line structured-block
Satsen är något av följande:
-
if(scalar-expression) -
private(variable-list) -
firstprivate(variable-list) default(shared | none)-
shared(variable-list) -
copyin(variable-list) -
reduction(operatör:variable-list) -
num_threads(heltalsuttryck)
När en tråd kommer till en parallell konstruktion skapas ett team med trådar om något av följande fall är sant:
- Det finns ingen
ifsats. - Uttrycket
ifutvärderas till ett värde som inte är noll.
Den här tråden blir huvudtråden i teamet, med ett trådnummer på 0, och alla trådar i teamet, inklusive huvudtråden, kör regionen parallellt. Om värdet för if uttrycket är noll serialiseras regionen.
För att fastställa antalet trådar som begärs kommer följande regler att beaktas i ordning. Den första regeln vars villkor uppfylls tillämpas:
num_threadsOm satsen finns är värdet för heltalsuttrycket antalet begärda trådar.omp_set_num_threadsOm biblioteksfunktionen har anropats är värdet för argumentet i det senast utförda anropet antalet begärda trådar.Om miljövariabeln
OMP_NUM_THREADShar definierats är värdet för den här miljövariabeln antalet begärda trådar.Om ingen av metoderna ovan används, är antalet begärda trådar definitionsmässigt beroende av implementeringen.
num_threads Om satsen finns ersätter den antalet trådar som begärs av omp_set_num_threads biblioteksfunktionen eller OMP_NUM_THREADS miljövariabeln endast för den parallella region som den tillämpas på. Senare parallella regioner påverkas inte av det.
Antalet trådar som kör den parallella regionen beror också på om dynamisk justering av antalet trådar är aktiverat. Om dynamisk justering är inaktiverad kör det begärda antalet trådar den parallella regionen. Om dynamisk justering är aktiverat är det begärda antalet trådar det maximala antalet trådar som kan köra den parallella regionen.
Om en parallell region påträffas när dynamisk justering av antalet trådar inaktiveras och antalet trådar som begärs för den parallella regionen är mer än det antal som körningssystemet kan ange, är programmets beteende implementeringsdefinierat. En implementering kan till exempel avbryta körningen av programmet eller serialisera den parallella regionen.
Biblioteksfunktionen omp_set_dynamic och OMP_DYNAMIC miljövariabeln kan användas för att aktivera och inaktivera dynamisk justering av antalet trådar.
Antalet fysiska processorer som faktiskt är värdar för trådarna vid en viss tidpunkt är implementeringsdefinierat. När det har skapats förblir antalet trådar i teamet konstant under hela den parallella regionen. Det kan ändras antingen explicit av användaren eller automatiskt av körningssystemet från en parallell region till en annan.
De uttalanden som finns inom den dynamiska omfattningen av den parallella regionen utförs av varje tråd, och varje tråd kan utföra en rad av uttalanden som skiljer sig från de andra trådarna. Direktiv som påträffas utanför den lexikala omfattningen av en parallell region kallas överblivna direktiv.
Det finns en underförstådd barriär i slutet av en parallell region. Endast huvudtråden i teamet fortsätter exekveringen i slutet av en parallell region.
Om en tråd i ett team som utför en parallell region stöter på en annan parallell konstruktion, skapar den ett nytt team och blir ledare för det nya teamet. Kapslade parallella regioner serialiseras som standard. Därför körs som standard en kapslad parallellregion av ett team som består av en tråd. Standardbeteendet kan ändras med hjälp av antingen körningsbiblioteksfunktionen omp_set_nested eller miljövariabeln OMP_NESTED. Men antalet trådar i ett team som kör en kapslad parallell region är implementeringsdefinierad.
Begränsningar i parallel direktivet är följande:
Som mest kan en
ifklausul finnas med i direktivet.Det är ospecificerat om några biverkningar inuti if-uttrycket eller
num_threadsuttrycket förekommer.En
throwsom körs i en parallell region måste leda till att körningen återupptas inom den dynamiska omfattningen av samma strukturerade block, och den måste fångas av samma tråd som utlöste undantaget.Endast en enda
num_threadsklausul kan visas i direktivet. Uttrycketnum_threadsutvärderas utanför kontexten för den parallella regionen och måste utvärderas till ett positivt heltalsvärde.Ordningen för utvärdering av
ifklausulerna ochnum_threadsär ospecificerad.
Korsreferenser
-
private,firstprivate,default,shared,copyinochreduction-satser (avsnitt 2.7.2) - OMP_NUM_THREADS miljövariabel
- omp_set_dynamic biblioteksfunktion
- OMP_DYNAMIC miljövariabel
- funktionen omp_set_nested
- OMP_NESTED miljövariabel
- omp_set_num_threads biblioteksfunktion
2.4 Konstruktioner för arbetsdelning
En arbetsdelningskonstruktion distribuerar utförandet av den associerade satsen bland medlemmarna i teamet som möter den. Direktiven för arbetsdelning startar inte nya trådar och det finns inget underförstått hinder för att komma in i en arbetsdelningskonstruktion.
Sekvensen med arbetsdelningskonstruktioner och barrier direktiv som påträffas måste vara densamma för varje tråd i ett team.
OpenMP definierar följande arbetsdelningskonstruktioner och dessa konstruktioner beskrivs i de avsnitt som följer:
- för direktiv
- direktiv för avsnitt
- enda direktiv
2.4.1 för konstruktion
Direktivet for identifierar en iterativ arbetsdelningskonstruktion som anger att iterationerna av den associerade loopen ska köras parallellt. Iterationerna av loopen for distribueras över trådar som redan finns i teamet som kör den parallella konstruktion som den binder till. Syntaxen för konstruktionen for är följande:
#pragma omp for [clause[[,] clause] ... ] new-line for-loop
Satsen är något av följande:
-
private(variable-list) -
firstprivate(variable-list) -
lastprivate(variable-list) -
reduction(operatör:variable-list) ordered-
schedule(kind [,chunk_size]) nowait
Direktivet for begränsar strukturen för motsvarande for loop. Mer specifikt måste motsvarande for loop ha kanonisk form:
for (
init-expr;var logical-op b;incr-expr)
init-expr
Något av följande:
- var = Lb
- heltalstyp var = Lb
incr-expr
Något av följande:
-
++var -
var
++ -
--var -
var
-- -
var
+=incr -
var
-=incr -
var
=var+incr -
var
=incr+var -
var
=var-incr
var
En signerad heltalsvariabel. Om den här variabeln annars skulle delas görs den implicit privat under varaktigheten för for. Ändra inte den här variabeln i instruktionens for brödtext. Om inte variabeln har angetts lastprivateär dess värde när loopen är obestämd.
logical-op
Något av följande:
<<=>>=
lb, b och incr
Loopinvarianta heltalsuttryck. Det finns ingen synkronisering under utvärderingen av dessa uttryck, så eventuella utvärderade biverkningar ger obestämda resultat.
Med det kanoniska formuläret kan antalet loop-iterationer beräknas vid inmatning till loopen. Den här beräkningen görs med värden i typen var, efter integrerade kampanjer. I synnerhet om värdet för b-lb+incr inte kan representeras i den typen, är resultatet obestämt. Om logical-op dessutom är < eller <=måste incr-expr orsaka att var ökar för varje iteration av loopen. Om logical-op är > eller >=måste incr-expr orsaka att var blir mindre för varje iteration av loopen.
Satsen schedule anger hur iterationer av loopen for delas mellan trådar i teamet. Korrektheten i ett program får inte bero på vilken tråd som kör en viss iteration. Värdet för chunk_size, om det anges, måste vara ett loop-invariant heltalsuttryck med ett positivt värde. Det finns ingen synkronisering under utvärderingen av det här uttrycket, så alla utvärderade biverkningar ger obestämda resultat. Schematyp typ kan vara något av följande värden:
Tabell 2-1: schedule sats slag värden
| Värde | Beskrivning |
|---|---|
| statiskt | När schedule(static,chunk_size) anges delas iterationer in i segment av en storlek som anges av chunk_size. Delarna fördelas statiskt till trådarna i teamet på ett roterande sätt i ordningen av trådnumret. När ingen chunk_size anges delas iterationsutrymmet upp i segment som är ungefär lika stora, med ett segment tilldelat till varje tråd. |
| dynamisk | När schedule(dynamic,chunk_size) anges delas iterationerna in i en serie segment som var och en innehåller chunk_size iterationer. Varje bit tilldelas en tråd som väntar på en uppgift. Tråden kör iterationssegmentet och väntar sedan på nästa tilldelning tills inga segment återstår att tilldela. Det sista segment som ska tilldelas kan ha ett mindre antal iterationer. När ingen chunk_size har angetts är standardvärdet 1. |
| Guidad | När schedule(guided,chunk_size) anges tilldelas iterationerna till trådar i segment med fallande storlekar. När en tråd har slutfört det tilldelade iterationssegmentet tilldelas den dynamiskt ett annat segment tills ingen är kvar. För en chunk_size på 1 är storleken på varje segment ungefär antalet otilldelade iterationer dividerat med antalet trådar. Dessa storlekar minskar nästan exponentiellt till 1. För en chunk_size med värdet k större än 1 minskar storlekarna nästan exponentiellt till k, förutom att det sista segmentet kan ha färre än k iterationer. När ingen chunk_size har angetts är standardvärdet 1. |
| Körningstid | När schedule(runtime) anges skjuts beslutet om schemaläggning upp tills programmet körs. Schematypen och storleken på segmenten kan väljas vid körning genom att ange miljövariabeln OMP_SCHEDULE. Om den här miljövariabeln inte har angetts är det resulterande schemat implementeringsdefinierat. När schedule(runtime) har angetts får chunk_size inte anges. |
I avsaknad av en explicit definierad sats schedule är standardinställningen schedule implementeringsdefinierad.
Ett OpenMP-kompatibelt program bör inte förlita sig på ett visst schema för korrekt exekvering. Ett program bör inte förlita sig på en schematyp som exakt överensstämmer med beskrivningen ovan, eftersom det är möjligt att ha variationer i implementeringarna av samma schematyp mellan olika kompilatorer. Beskrivningarna kan användas för att välja det schema som är lämpligt för en viss situation.
Klausulen ordered måste finnas när ordered direktiv binder till konstruktionen for .
Det finns en implicit barriär i slutet av en for konstruktion om inte en nowait sats har angetts.
Begränsningar i for direktivet är följande:
Loopen
formåste vara ett strukturerat block och dessutom får dess körning inte avslutas av enbreak-instruktion.Kontrolluttryckens värden i den
for-loop som är associerad medfor-direktivet måste vara desamma för alla trådar i teamet.Loop-iterationsvariabeln
formåste ha en signerad heltalstyp.Endast en enda
schedulesats kan visas i ettfordirektiv.Endast en enda
orderedsats kan visas i ettfordirektiv.Endast en enda
nowaitsats kan visas i ettfordirektiv.Det är ospecificerat om eller hur ofta några biverkningar inom chunk_size, lb, b eller incr uttryck förekommer.
Värdet för chunk_size-uttrycket måste vara detsamma för alla trådar i teamet.
Korsreferenser
-
private,firstprivate,lastprivateochreduction-satser (avsnitt 2.7.2) - OMP_SCHEDULE miljövariabel
- ordnad konstruktion
- schema klausul
2.4.2 avsnittskonstruktion
Direktivet sections identifierar en icke-iterativ arbetsdelningskonstruktion som anger en uppsättning konstruktioner som ska delas upp mellan trådar i ett team. Varje avsnitt körs en gång av en tråd i teamet. Syntaxen för sections direktivet är följande:
#pragma omp sections [clause[[,] clause] ...] new-line
{
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
...
}
Satsen är något av följande:
-
private(variable-list) -
firstprivate(variable-list) -
lastprivate(variable-list) -
reduction(operatör:variable-list) nowait
Varje avsnitt föregås av ett section direktiv, även om section direktivet är valfritt för det första avsnittet. Direktivet section måste förekomma inom den lexikala omfattningen av direktivet sections. Det finns en implicit barriär i slutet av en sections konstruktion, såvida inte en nowait har angetts.
Begränsningar i sections direktivet är följande:
Ett
sectiondirektiv får inte förekomma utanför direktivetssectionslexikala omfattning.Endast en enda
nowaitsats kan visas i ettsectionsdirektiv.
Korsreferenser
-
private,firstprivate,lastprivateochreduction-satser (avsnitt 2.7.2)
2.4.3 enkel konstruktion
Direktivet single identifierar en konstruktion som anger att det associerade strukturerade blocket endast körs av en tråd i teamet (inte nödvändigtvis huvudtråden). Syntaxen för single direktivet är följande:
#pragma omp single [clause[[,] clause] ...] new-linestructured-block
Satsen är något av följande:
-
private(variable-list) -
firstprivate(variable-list) -
copyprivate(variable-list) nowait
Det finns en implicit barriär efter konstruktionen single om inte en nowait sats har angetts.
Begränsningar i single direktivet är följande:
- Endast en enda
nowaitsats kan visas i ettsingledirektiv. - Satsen
copyprivatefår inte användas mednowait-satsen.
Korsreferenser
-
private,firstprivateochcopyprivate-satser (avsnitt 2.7.2)
2.5 Kombinerade parallella konstruktioner för arbetsdelning
Kombinerade parallella arbetsdelningskonstruktioner är genvägar för att ange en parallell region som bara har en arbetsdelningskonstruktion. Semantiken i dessa direktiv är densamma som att uttryckligen ange ett parallel direktiv följt av en enda konstruktion för arbetsdelning.
I följande avsnitt beskrivs de kombinerade parallella konstruktionerna för arbetsdelning:
2.5.1 parallell för konstruktion
Direktivet parallel for är en genväg för en parallel region som endast innehåller ett enda for direktiv. Syntaxen för parallel for direktivet är följande:
#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop
Detta direktiv tillåter alla klausuler i parallel direktivet och for direktivet, förutom klausulen nowait , med identiska betydelser och begränsningar. Semantiken är densamma som att uttryckligen ange ett parallel direktiv omedelbart följt av ett for direktiv.
Korsreferenser
- parallella direktiv
- för direktiv
- Dataattributsatser
2.5.2 parallella sektioner konstruktion
Direktivet parallel sections innehåller ett genvägsformulär för att ange en parallel region som bara har ett enda sections direktiv. Semantiken är densamma som att uttryckligen ange ett parallel direktiv omedelbart följt av ett sections direktiv. Syntaxen för parallel sections direktivet är följande:
#pragma omp parallel sections [clause[[,] clause] ...] new-line
{
[#pragma omp section new-line]
structured-block
[#pragma omp section new-linestructured-block ]
...
}
Klausulen kan vara en av de klausuler som godkänns av direktiven parallel ochsections, förutom klausulennowait.
Korsreferenser
- parallella direktiv
- direktiv för avsnitt
2.6 Huvud- och synkroniseringsdirektiv
Följande avsnitt beskriver:
- huvudkonstruktion
- viktig konstruktion
- barriärdirektiv
- atomär konstruktion
- Flush-direktiv
- ordnad konstruktion
2.6.1 huvudkonstruktion
Direktivet master identifierar en konstruktion som anger ett strukturerat block som körs av teamets huvudtråd. Syntaxen för master direktivet är följande:
#pragma omp master new-linestructured-block
Andra trådar i teamet kör inte det associerade strukturerade blocket. Det finns ingen underförstådd barriär vid in- eller utgång från huvudstrukturen.
2.6.2 kritisk konstruktion
Direktivet critical identifierar en konstruktion som begränsar körningen av det associerade strukturerade blocket till en enda tråd i taget. Syntaxen för critical direktivet är följande:
#pragma omp critical [(name)] new-linestructured-block
Ett valfritt namn kan användas för att identifiera den kritiska regionen. Identifierare som används för att identifiera en kritisk region har extern länkning och finns i ett namnutrymme som är separat från de namnutrymmen som används av etiketter, taggar, medlemmar och vanliga identifierare.
En tråd väntar i början av en kritisk region tills ingen annan tråd kör en kritisk region (var som helst i programmet) med samma namn. Alla namnlösa critical direktiv mappas till samma ospecificerade namn.
2.6.3 Barriärdirektiv
Direktivet barrier synkroniserar alla trådar i ett team. När de påträffas väntar varje tråd i teamet tills alla andra har nått den här punkten. Syntaxen för barrier direktivet är följande:
#pragma omp barrier new-line
När alla trådar i teamet har stött på barriären börjar varje tråd i teamet köra instruktionerna efter barriärdirektivet parallellt.
barrier Eftersom direktivet inte har en C-språksats som en del av dess syntax finns det vissa begränsningar för dess placering i ett program. Mer information om den formella grammatiken finns i bilaga C. Exemplet nedan illustrerar dessa begränsningar.
/* 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 Atomär konstruktion
Direktivet atomic säkerställer att en specifik minnesplats uppdateras atomiskt, i stället för att exponera den för möjligheten till flera samtidiga skrivtrådar. Syntaxen för atomic direktivet är följande:
#pragma omp atomic new-lineexpression-stmt
Uttryckssatsen måste ha något av följande formulär:
-
x binop
=expr -
x
++ -
++x -
x
-- -
--x
I föregående uttryck:
x är ett lvalue-uttryck med skalär typ.
expr är ett uttryck med skalär typ och refererar inte till objektet som anges av x.
binop är inte en överlagrad operator och är en av
+,*,-,/,&,^,|,<<, eller>>.
Även om det är implementeringsdefinierat om en implementering ersätter alla atomic direktiv med critical direktiv som har samma unika namn, atomic tillåter direktivet bättre optimering. Maskinvaruinstruktioner är ofta tillgängliga som kan utföra atomuppdateringen med minst omkostnader.
Endast inläsningen och lagringen av objektet som anges av x är atomiska; utvärderingen av expr är inte atomisk. För att undvika tävlingsförhållanden bör alla uppdateringar av positionen i parallella processer skyddas med atomic direktivet, förutom de som säkert är fria från tävlingsförhållanden.
Begränsningar i atomic direktivet är följande:
- Alla atomiska referenser till lagringsplatsen x i hela programmet måste ha en kompatibel typ.
Exempel
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 spolningsdirektiv
Direktivet flush , oavsett om det är explicit eller underförstått, anger en sekvenspunkt mellan trådar där implementeringen krävs för att säkerställa att alla trådar i ett team har en konsekvent vy över vissa objekt (anges nedan) i minnet. Det innebär att tidigare utvärderingar av uttryck som refererar till dessa objekt är slutförda och att efterföljande utvärderingar ännu inte har påbörjats. Kompilatorer måste till exempel återställa objektens värden från register till minne, och maskinvaran kan behöva rensa skrivbuffertar till minnet och läsa in objektens värden från minnet igen.
Syntaxen för flush direktivet är följande:
#pragma omp flush [(variable-list)] new-line
Om alla objekt som kräver synkronisering kan anges av variabler kan dessa variabler anges i den valfria variabellistan. Om en pekare finns i variabellistan rensas själva pekaren, inte det objekt som pekaren refererar till.
Ett flush direktiv utan en variabellista synkroniserar alla delade objekt utom otillgängliga objekt med automatisk lagringstid. (Detta kommer sannolikt att ha mer omkostnader än en flush med en variabellista.) Ett flush direktiv utan en variabellista är underförstått för följande direktiv:
barrier- Vid inträde till och utträde från
critical - Vid inträde till och utträde från
ordered - Vid inträde till och utträde från
parallel - Vid utgång från
for - Vid utgång från
sections - Vid utgång från
single - Vid inträde till och utträde från
parallel for - Vid inträde till och utträde från
parallel sections
Direktivet är inte underförstått om det finns en nowait klausul. Det bör noteras att flush direktivet inte är underförstått för något av följande:
- Vid inträde till
for - Vid inträde till eller utträde från
master - Vid inträde till
sections - Vid inträde till
single
En referens som får åtkomst till värdet för ett objekt med en flyktig kvalificerad typ beter sig som om det fanns ett flush direktiv som anger objektet vid föregående sekvenspunkt. En referens som ändrar värdet för ett objekt med en flyktig kvalificerad typ beter sig som om det fanns ett flush direktiv som anger objektet vid den efterföljande sekvenspunkten.
flush Eftersom direktivet inte har en C-språksats som en del av dess syntax finns det vissa begränsningar för dess placering i ett program. Mer information om den formella grammatiken finns i bilaga C. Exemplet nedan illustrerar dessa begränsningar.
/* 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)
}
Begränsningar i flush direktivet är följande:
- En variabel som anges i ett
flushdirektiv får inte ha någon referenstyp.
2.6.6 beställd konstruktion
Det strukturerade blocket efter ett ordered-direktiv körs i den ordning som iterationerna skulle utföras i en sekventiell loop. Syntaxen för ordered direktivet är följande:
#pragma omp ordered new-linestructured-block
Ett ordered direktiv måste ligga inom den dynamiska omfattningen av en for eller parallel for konstruktionen. Det for eller-direktiv parallel for som konstruktionen ordered binder till måste ha en ordered klausul som anges enligt beskrivningen i avsnitt 2.4.1. Vid körning av en for eller parallel for konstruktion med en ordered-sats utförs ordered-konstruktionerna strikt i den ordning som de skulle utföras vid en sekventiell körning av loopens.
Begränsningar i ordered direktivet är följande:
- En iteration av en loop med en
forkonstruktion får inte köra samma ordnade direktiv mer än en gång, och den får inte köra mer än ettordereddirektiv.
2.7 Datamiljö
I det här avsnittet presenteras ett direktiv och flera satser för att kontrollera datamiljön under körningen av parallella regioner enligt följande:
Ett threadprivate-direktiv tillhandahålls för att göra filomfattning, namnområdesomfång eller statiska blockomfattningsvariabler lokala för en tråd.
Klausuler som kan anges i direktiven för att styra delningsattributen för variabler under varaktigheten för parallella konstruktioner eller arbetsdelningskonstruktioner beskrivs i avsnitt 2.7.2.
2.7.1 trådprivate-direktiv
Direktivet threadprivate gör de namngivna variablerna file-scope, namespace-scope eller static block-scope som anges i variabellistan privata för en tråd.
variable-list är en kommaavgränsad lista med variabler som inte har en ofullständig typ. Syntaxen för threadprivate direktivet är följande:
#pragma omp threadprivate(variable-list) new-line
Varje kopia av en threadprivate variabel initieras en gång, vid en ospecificerad punkt i programmet före den första referensen till kopian, och på vanligt sätt (d.v.s. eftersom huvudkopian initieras i en seriell körning av programmet). Observera att om ett objekt refereras till i en explicit initiering av en threadprivate variabel och värdet för objektet ändras innan den första referensen till en kopia av variabeln, så är beteendet ospecificerat.
Precis som med alla privata variabler får en tråd inte referera till en annan tråds kopia av ett threadprivate objekt. Under serieregioner och huvudregioner i programmet kommer referenser att vara till huvudtrådens kopia av objektet.
När den första parallella regionen har körts bevaras data i objekten threadprivate endast om mekanismen för dynamiska trådar har inaktiverats och om antalet trådar förblir oförändrat för alla parallella regioner.
Begränsningarna i threadprivate direktivet är följande:
Ett
threadprivatedirektiv för variabler med filomfattning eller namnområdesomfång måste visas utanför någon definition eller deklaration och måste lexikalt föregå alla referenser till någon av variablerna i listan.Varje variabel i variabellistan för ett
threadprivatedirektiv i fil- eller namnområdesomfånget måste referera till en variabeldeklaration i fil- eller namnområdesomfånget som lexikalt föregår direktivet.Ett
threadprivatedirektiv för variabler med statiskt blockomfång måste visas i variabelns omfång och inte i ett kapslat omfång. Direktivet måste lexikalt föregå alla hänvisningar till någon av variablerna i sin lista.Varje variabel i variabellistan för ett
threadprivatedirektiv i blockomfånget måste referera till en variabeldeklaration i samma omfång som lexikalt föregår direktivet. Variabeldeklarationen måste använda den statiska lagringsklasssspecificeraren.Om en variabel anges i ett
threadprivatedirektiv i en översättningsenhet måste den anges i ettthreadprivatedirektiv i varje översättningsenhet där den deklareras.En
threadprivate-variabel får inte visas i någon sats förutom icopyin-satsen,copyprivate,schedule,num_threadsellerif-satsen.Adressen för en
threadprivatevariabel är inte en adresskonstant.En
threadprivatevariabel får inte ha en ofullständig typ eller en referenstyp.En
threadprivatevariabel med icke-POD-klasstyp måste ha en tillgänglig, entydig kopieringskonstruktor om den deklareras med en explicit initialiserare.
I följande exempel visas hur ändring av en variabel som visas i en initiator kan orsaka ospecificerat beteende och även hur du undviker det här problemet med hjälp av ett hjälpobjekt och en kopieringskonstruktor.
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. */
}
}
Korsreferenser
- dynamiska trådar
- OMP_DYNAMIC miljövariabel
2.7.2 Datadelningsattributsatser
Flera direktiv accepterar satser som gör det möjligt för en användare att styra delningsattributen för variabler under hela regionen. Delningsattributklausuler gäller endast för variabler i den lexikala omfattningen av det direktiv som klausulen förekommer på. Alla följande klausuler är inte tillåtna i alla direktiv. Listan över klausuler som är giltiga för ett visst direktiv beskrivs i direktivet.
Om en variabel visas när en parallell konstruktion eller arbetsdelningskonstruktion påträffas och variabeln inte anges i en delningsattributsats eller threadprivate ett direktiv delas variabeln. Statiska variabler som deklareras inom den dynamiska omfattningen för en parallell region delas. Heap-allokerat minne (till exempel genom att använda malloc() i C eller C++ eller operatorn new i C++) delas. (Pekaren till det här minnet kan dock vara privat eller delad.) Variabler med automatisk lagringstid som deklareras inom den dynamiska omfattningen för en parallell region är privata.
De flesta av satserna accepterar ett variabel-list-argument , vilket är en kommaavgränsad lista över variabler som är synliga. Om en variabel som refereras till i en datadelningsattributsats har en typ som härleds från en mall och det inte finns några andra referenser till variabeln i programmet, är beteendet odefinierat.
Alla variabler som visas i direktivsatser måste vara synliga. Satser kan upprepas efter behov, men ingen variabel kan anges i mer än en sats, förutom att en variabel kan anges i både en firstprivate och en lastprivate -sats.
I följande avsnitt beskrivs satserna för datadelningsattribut:
2.7.2.1 privat
Satsen private deklarerar variablerna i variabellistan som privata för varje tråd i ett team. Syntaxen för private -satsen är följande:
private(variable-list)
Beteendet för en variabel som anges i en private sats är följande. Ett nytt objekt med automatisk lagringstid allokeras för konstruktionen. Storleken och justeringen för det nya objektet bestäms av variabeltypen. Den här allokeringen sker en gång för varje tråd i teamet och en standardkonstruktor anropas för ett klassobjekt om det behövs. annars är det initiala värdet obestämt. Det ursprungliga objektet som refereras av variabeln har ett obestämt värde vid inmatning till konstruktionen, får inte ändras inom den dynamiska omfattningen av konstruktionen och har ett obestämt värde när konstruktionen avslutas.
I den lexikala omfattningen av direktivkonstruktionen refererar variabeln till det nya privata objekt som allokeras av tråden.
Begränsningarna för private satsen är följande:
En variabel med en klasstyp som anges i en
privatesats måste ha en tillgänglig, otvetydig standardkonstruktor.En variabel som anges i en
privatesats får inte ha enconst-kvalificerad typ om den inte har en klasstyp med enmutablemedlem.En variabel som anges i en
privatesats får inte ha en ofullständig typ eller en referenstyp.Variabler som förekommer i
reduction-satsen av ettparallel-direktiv kan inte specificeras iprivate-satsen i ett arbetsdelningsdirektiv som binds till den parallella konstruktionen.
2.7.2.2 firstprivate
- firstprivate satsen tillhandahåller en superuppsättning av de funktioner som tillhandahålls av private -satsen. Syntaxen för firstprivate -satsen är följande:
firstprivate(variable-list)
Variabler som anges i variabellistan har private satssemantik enligt beskrivningen i avsnitt 2.7.2.1. Initieringen eller konstruktionen sker som om den utfördes en gång per tråd, innan tråden börjar köra konstruktionen. För en firstprivate sats i en parallell konstruktion är det ursprungliga värdet för det nya privata objektet värdet för det ursprungliga objektet som finns omedelbart före den parallella konstruktionen för tråden som stöter på det. För en firstprivate sats i en arbetsdelningskonstruktion är det ursprungliga värdet för det nya privata objektet för varje tråd som kör arbetsdelningskonstruktionen värdet för det ursprungliga objektet som finns före den tidpunkt då samma tråd stöter på arbetsdelningskonstruktionen. För C++-objekt kopieras dessutom det nya privata objektet för varje tråd från det ursprungliga objektet.
Begränsningarna för firstprivate satsen är följande:
En variabel som anges i en
firstprivatesats får inte ha en ofullständig typ eller en referenstyp.En variabel med en klasstyp som anges som
firstprivatemåste ha en tillgänglig, entydig kopieringskonstruktor.Variabler som är privata inom en parallell region eller som visas i satsen i
reductionettparalleldirektiv kan inte anges i enfirstprivatesats i ett arbetsdelningsdirektiv som binder till den parallella konstruktionen.
2.7.2.3 lastprivate
- lastprivate satsen tillhandahåller en superuppsättning av de funktioner som tillhandahålls av private -satsen. Syntaxen för lastprivate -satsen är följande:
lastprivate(variable-list)
Variabler som anges i variabellistan har private satssemantik. När en lastprivate sats visas i direktivet som identifierar en arbetsdelningskonstruktion tilldelas värdet för varje lastprivate variabel från den sekventiellt sista iterationen av den associerade loopen eller det lexikaliskt sista avsnittsdirektivet till variabelns ursprungliga objekt. Variabler som inte tilldelas ett värde av den senaste iterationen for av eller parallel for, eller av det lexikalt sista avsnittet i sections eller parallel sections -direktivet, har obestämda värden efter konstruktionen. Otilldelade underobjekt har också ett obestämt värde efter konstruktionen.
Begränsningarna för lastprivate satsen är följande:
Alla begränsningar för
privategäller.En variabel med en klasstyp som anges som
lastprivatemåste ha en tillgänglig, entydig kopieringstilldelningsoperator.Variabler som är privata inom en parallell region eller som visas i satsen i
reductionettparalleldirektiv kan inte anges i enlastprivatesats i ett arbetsdelningsdirektiv som binder till den parallella konstruktionen.
2.7.2.4 delat
Den här satsen delar variabler som visas i variabellistan bland alla trådar i ett team. Alla trådar i ett team har åtkomst till samma lagringsområde för shared variabler.
Syntaxen för shared -satsen är följande:
shared(variable-list)
2.7.2.5 förval
default Satsen gör det möjligt för användaren att påverka datadelningsattributen för variabler. Syntaxen för default -satsen är följande:
default(shared | none)
Att specificera default(shared) motsvarar att uttryckligen lista varje för närvarande synlig variabel i en shared-sats, såvida den inte är threadprivate- eller const-kvalificerad. I avsaknad av en explicit default sats är standardbeteendet detsamma som om det default(shared) angavs.
För att default(none) ange krävs att minst något av följande måste vara sant för varje referens till en variabel i den lexikala omfattningen av den parallella konstruktionen:
Variabeln visas uttryckligen i en datadelningsattributsats i en konstruktion som innehåller referensen.
Variabeln deklareras inom den parallella konstruktionen.
Variabeln är
threadprivate.Variabeln har en
const-kvalificerad typ.Variabeln är loopkontrollvariabeln för en
forloop som omedelbart följer ettforellerparallel for-direktiv, och variabelreferensen visas i loopen.
Om du anger en variabel för en firstprivate, lastprivate, eller reduction -sats i ett omslutet direktiv får du en implicit referens till variabeln i den omslutande kontexten. Sådana implicita referenser omfattas också av de krav som anges ovan.
Endast en enda default sats får anges i ett parallel direktiv.
En variabels standardattribut för datadelning kan åsidosättas med hjälp av satserna private, firstprivate, lastprivate, reductionoch shared som visas i följande exempel:
#pragma omp parallel for default(shared) firstprivate(i)\
private(x) private(r) lastprivate(i)
2.7.2.6 minskning
Den här satsen utför en minskning av de skalärvariabler som visas i variabellistanmed operator op. Syntaxen för reduction -satsen är följande:
reduction(
Op:variable-list)
En minskning anges vanligtvis för ett påstående med någon av följande former:
-
x
=xopexpr -
xbinop
=expr -
x
=expropx (förutom subtraktion) -
x
++ -
++x -
x
-- -
--x
där:
x
En av de minskningsvariabler som anges i listan.
variable-list
En kommaavgränsad lista över skalära minskningsvariabler.
expr
Ett uttryck med skalär typ som inte refererar till x.
Op
Inte en överbelastad operator utan en av +, *, -, &, ^, |, && eller ||.
binop
Inte en överbelastad operator utan en av +, *, -, &, ^ eller |.
Följande är ett exempel på reduction satsen:
#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];
}
Som du ser i exemplet kan en operator vara dold i ett funktionsanrop. Användaren bör vara försiktig med att operatorn som anges i reduction -satsen matchar minskningsåtgärden.
Även om operatorns || högra operand inte har några bieffekter i det här exemplet, är de tillåtna, men bör användas med försiktighet. I det här sammanhanget kan en bieffekt som garanterat inte inträffar under sekventiell körning av loopen inträffa under parallell körning. Den här skillnaden kan inträffa eftersom körningsordningen för iterationerna är obestämd.
Operatorn används för att fastställa det ursprungliga värdet för alla privata variabler som används av kompilatorn för minskningen och för att fastställa slutförandeoperatorn. Om du anger operatorn uttryckligen kan minskningssatsen ligga utanför konstruktionens lexikala omfattning. Valfritt antal reduction klausuler kan anges i direktivet, men en variabel kan förekomma i högst en reduction klausul för det direktivet.
En privat kopia av varje variabel i variabellistan skapas, en för varje tråd, som om private satsen hade använts. Privatkopian initieras i enlighet med operatören (se följande tabell).
I slutet av den region där reduction satsen angavs uppdateras det ursprungliga objektet så att det återspeglar resultatet av att kombinera dess ursprungliga värde med det slutliga värdet för var och en av de privata kopiorna med den angivna operatorn. Minskningsoperatorerna är alla associativa (förutom subtraktion), och kompilatorn kan fritt återassociera beräkningen av det slutliga värdet. (De partiella resultaten av en subtraktionsreduktion läggs till för att bilda det slutliga värdet.)
Värdet för det ursprungliga objektet blir obestämt när den första tråden når den innehållande satsen och förblir så tills minskningsberäkningen är klar. Normalt slutförs beräkningen i slutet av konstruktionen. Men om reduction -satsen används på en konstruktion som nowait också tillämpas förblir värdet för det ursprungliga objektet obestämt tills en barriärsynkronisering har utförts för att säkerställa att alla trådar har slutfört reduction satsen.
I följande tabell visas de operatorer som är giltiga och deras kanoniska initieringsvärden. Det faktiska initieringsvärdet överensstämmer med datatypen för minskningsvariabeln.
| Operatör | Initialisering |
|---|---|
+ |
0 |
* |
1 |
- |
0 |
& |
~0 |
| |
0 |
^ |
0 |
&& |
1 |
|| |
0 |
Begränsningarna för reduction satsen är följande:
Typen av variabler i
reduction-satsen måste vara giltig för reduceringsoperatorn, förutom att pekartyper och referenstyper aldrig tillåts.En variabel som anges i
reduction-klausulen får inte varaconst-kvalificerad.Variabler som är privata inom en parallell region eller som visas i satsen i
reductionettparalleldirektiv kan inte anges i enreductionsats i ett arbetsdelningsdirektiv som binder till den parallella konstruktionen.#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
copyin Satsen tillhandahåller en mekanism för att tilldela samma värde till threadprivate variabler för varje tråd i teamet som kör den parallella regionen. För varje variabel som anges i en copyin sats kopieras värdet för variabeln i teamets huvudtråd, som om det vore en tilldelning, till de tråd-privata kopiorna i början av den parallella regionen. Syntaxen för copyin -satsen är följande:
copyin(
variable-list
)
Begränsningarna för copyin satsen är följande:
En variabel som anges i
copyin-satsen måste ha en tillgänglig, entydig kopieringstilldelningsoperator.En variabel som anges i
copyin-satsen måste vara enthreadprivatevariabel.
2.7.2.8 copyprivate
Satsen copyprivate tillhandahåller en mekanism för att använda en privat variabel för att sända ett värde från en medlem i ett team till de andra medlemmarna. Det är ett alternativ till att använda en gemensam variabel för värdet när det är svårt att tillhandahålla en sådan (till exempel i en rekursion som kräver en annan variabel på varje nivå). Klausulen copyprivate kan bara finnas med i single direktivet.
Syntaxen för copyprivate -satsen är följande:
copyprivate(
variable-list
)
Effekten av copyprivate-satsen på variablerna i variabellistan inträffar efter att det strukturerade blocket har körts som är associerat med konstruktionen single, och innan teamets trådar har lämnat barriären i slutet av konstruktionen. I alla andra trådar i teamet, för varje variabel i variabellistan, definieras variabeln (som vid tilldelning) med värdet för motsvarande variabel i tråden som körde konstruktionens strukturerade block.
Begränsningar för copyprivate satsen är följande:
En variabel som anges i
copyprivate-satsen får inte visas i enprivate-sats ellerfirstprivate-sats för sammasingle-direktiv.Om ett
singledirektiv med encopyprivatesats påträffas i den dynamiska omfattningen av en parallell region måste alla variabler som anges icopyprivatesatsen vara privata i omslutande kontext.En variabel som anges i
copyprivate-satsen måste ha en tillgänglig entydig kopieringstilldelningsoperator.
2.8 Direktivbindning
Dynamisk bindning av direktiv måste följa följande regler:
Direktiven
for,sections,single,masterochbarrierbinder till den dynamiskt omslutandeparallel, om det finns någon, oavsett värdet av eventuellaifklausuler som kan finnas i det direktivet. Om ingen parallell region körs för närvarande, körs direktiven av ett team bestående endast av huvudtråden.Direktivet
orderedbinder till den dynamiskt omslutandefor.Direktivet
atomictillämpar exklusiv åtkomst i förhållande tillatomic-direktiv på alla trådar, och inte bara det nuvarande laget.Direktivet
criticaltillämpar exklusiv åtkomst i förhållande tillcritical-direktiv på alla trådar, och inte bara det nuvarande laget.Ett direktiv kan aldrig binda till något direktiv utanför den närmaste dynamiskt omslutande
parallel.
2.9 Kapsling av direktiv
Dynamisk kapsling av direktiv måste följa följande regler:
Ett
paralleldirektiv dynamiskt i en annanparalleletablerar logiskt ett nytt team, som endast består av den aktuella tråden, om inte kapslad parallellitet är aktiverad.for,sections, ochsingledirektiv som binder till sammaparallelfår inte kapslas i varandra.criticaldirektiv med samma namn får inte kapslas i varandra. Observera att den här begränsningen inte räcker för att förhindra dödläge.for,sections, ochsingledirektiv tillåts inte i den dynamiska omfattningen avcritical,orderedochmasterregioner om direktiven binder till sammaparallelsom regionerna.barrierdirektiv tillåts inte i den dynamiska omfattningen avfor,ordered,sections,single,masterochcriticalregioner om direktiven binder till sammaparallelsom regionerna.masterdirektiv tillåts inte i den dynamiska omfattningen avfor,sectionsochsingledirektiv ommasterdirektiven binder till sammaparallelsom direktiven för arbetsdelning.ordered-direktiv tillåts inte inom det dynamiska omfånget avcritical-regioner om direktiven binder till sammaparallelsom regionerna.Alla direktiv som tillåts när de körs dynamiskt i en parallell region tillåts också när de körs utanför en parallell region. När det körs dynamiskt utanför en användardefinerad parallell region körs direktivet av ett team som endast består av huvudtråden.