Dela via


2. Direktiv

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 if sats.
  • Uttrycket if utvä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:

  1. num_threads Om satsen finns är värdet för heltalsuttrycket antalet begärda trådar.

  2. omp_set_num_threads Om biblioteksfunktionen har anropats är värdet för argumentet i det senast utförda anropet antalet begärda trådar.

  3. Om miljövariabeln OMP_NUM_THREADS har definierats är värdet för den här miljövariabeln antalet begärda trådar.

  4. 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 if klausul finnas med i direktivet.

  • Det är ospecificerat om några biverkningar inuti if-uttrycket eller num_threads uttrycket förekommer.

  • En throw som 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_threads klausul kan visas i direktivet. Uttrycket num_threads utvä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 if klausulerna och num_threads är ospecificerad.

Korsreferenser

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:

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 for måste vara ett strukturerat block och dessutom får dess körning inte avslutas av en break -instruktion.

  • Kontrolluttryckens värden i den for-loop som är associerad med for-direktivet måste vara desamma för alla trådar i teamet.

  • Loop-iterationsvariabeln for måste ha en signerad heltalstyp.

  • Endast en enda schedule sats kan visas i ett for direktiv.

  • Endast en enda ordered sats kan visas i ett for direktiv.

  • Endast en enda nowait sats kan visas i ett for direktiv.

  • 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

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 section direktiv får inte förekomma utanför direktivets sections lexikala omfattning.

  • Endast en enda nowait sats kan visas i ett sections direktiv.

Korsreferenser

  • private, firstprivate, lastprivateoch reduction -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 nowait sats kan visas i ett single direktiv.
  • Satsen copyprivate får inte användas med nowait -satsen.

Korsreferenser

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

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

2.6 Huvud- och synkroniseringsdirektiv

Följande avsnitt beskriver:

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 flush direktiv 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 for konstruktion får inte köra samma ordnade direktiv mer än en gång, och den får inte köra mer än ett ordered direktiv.

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 threadprivate direktiv 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 threadprivate direktiv 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 threadprivate direktiv 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 threadprivate direktiv 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 threadprivate direktiv i en översättningsenhet måste den anges i ett threadprivate direktiv i varje översättningsenhet där den deklareras.

  • En threadprivate-variabel får inte visas i någon sats förutom i copyin-satsen, copyprivate, schedule, num_threads eller if-satsen.

  • Adressen för en threadprivate variabel är inte en adresskonstant.

  • En threadprivate variabel får inte ha en ofullständig typ eller en referenstyp.

  • En threadprivate variabel 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

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 private sats måste ha en tillgänglig, otvetydig standardkonstruktor.

  • En variabel som anges i en private sats får inte ha en const-kvalificerad typ om den inte har en klasstyp med en mutable medlem.

  • En variabel som anges i en private sats får inte ha en ofullständig typ eller en referenstyp.

  • Variabler som förekommer i reduction-satsen av ett parallel-direktiv kan inte specificeras i private-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 firstprivate sats får inte ha en ofullständig typ eller en referenstyp.

  • En variabel med en klasstyp som anges som firstprivate måste ha en tillgänglig, entydig kopieringskonstruktor.

  • Variabler som är privata inom en parallell region eller som visas i satsen i reduction ett parallel direktiv kan inte anges i en firstprivate sats 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 private gäller.

  • En variabel med en klasstyp som anges som lastprivate måste ha en tillgänglig, entydig kopieringstilldelningsoperator.

  • Variabler som är privata inom en parallell region eller som visas i satsen i reduction ett parallel direktiv kan inte anges i en lastprivate sats 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 for loop som omedelbart följer ett for eller parallel 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 vara const-kvalificerad.

  • Variabler som är privata inom en parallell region eller som visas i satsen i reduction ett parallel direktiv kan inte anges i en reduction sats 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 en threadprivate variabel.

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 en private-sats eller firstprivate-sats för samma single-direktiv.

  • Om ett single direktiv med en copyprivate sats påträffas i den dynamiska omfattningen av en parallell region måste alla variabler som anges i copyprivate satsen 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, masteroch barrier binder till den dynamiskt omslutande parallel, om det finns någon, oavsett värdet av eventuella if klausuler 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 ordered binder till den dynamiskt omslutande for.

  • Direktivet atomic tillämpar exklusiv åtkomst i förhållande till atomic-direktiv på alla trådar, och inte bara det nuvarande laget.

  • Direktivet critical tillämpar exklusiv åtkomst i förhållande till critical-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 parallel direktiv dynamiskt i en annan parallel etablerar logiskt ett nytt team, som endast består av den aktuella tråden, om inte kapslad parallellitet är aktiverad.

  • for, sections, och single direktiv som binder till samma parallel får inte kapslas i varandra.

  • critical direktiv 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, och single direktiv tillåts inte i den dynamiska omfattningen av critical, orderedoch master regioner om direktiven binder till samma parallel som regionerna.

  • barrier direktiv tillåts inte i den dynamiska omfattningen av for, ordered, sections, single, masteroch critical regioner om direktiven binder till samma parallel som regionerna.

  • master direktiv tillåts inte i den dynamiska omfattningen av for, sections och single direktiv om master direktiven binder till samma parallel som direktiven för arbetsdelning.

  • ordered -direktiv tillåts inte inom det dynamiska omfånget av critical-regioner om direktiven binder till samma parallel som 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.