Not
Å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.
Följande är exempel på de konstruktioner som definierats i det här dokumentet. Ett påstående som följer ett direktiv är komplex endast när det är nödvändigt, och ett icke-komplex påstående indenteras från ett direktiv som föregår det.
A.1 En enkel loop parallellt
I följande exempel visas hur du parallelliserar en loop med hjälp av parallellen för direktivet. Loop-iterationsvariabeln är privat som standard, så det är inte nödvändigt att uttryckligen ange den i en privat sats.
#pragma omp parallel for
for (i=1; i<n; i++)
b[i] = (a[i] + a[i-1]) / 2.0;
A.2 Villkorlig kompilering
Följande exempel illustrerar användningen av villkorsstyrd kompilering med hjälp av OpenMP-makrot _OPENMP. Med OpenMP-kompilering definieras makrot _OPENMP.
# ifdef _OPENMP
printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif
Med den definierade operatorn för förprocessorn kan fler än ett makro testas i ett enda direktiv.
# if defined(_OPENMP) && defined(VERBOSE)
printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif
A.3 Parallella regioner
Parallelldirektivet kan användas i grova parallella program. I följande exempel avgör varje tråd i den parallella regionen vilken del av den globala matrisen x som ska arbeta med, baserat på trådnumret:
#pragma omp parallel shared(x, npoints) private(iam, np, ipoints)
{
iam = omp_get_thread_num();
np = omp_get_num_threads();
ipoints = npoints / np;
subdomain(x, iam, ipoints);
}
A.4 Nowait-satsen
Om det finns många oberoende loopar inom en parallell region kan du använda nowait-satsen för att undvika den underförstådda barriären for i slutet av direktivet på följande sätt:
#pragma omp parallel
{
#pragma omp for nowait
for (i=1; i<n; i++)
b[i] = (a[i] + a[i-1]) / 2.0;
#pragma omp for nowait
for (i=0; i<m; i++)
y[i] = sqrt(z[i]);
}
A.5 Det kritiska direktivet
Följande exempel innehåller flera kritiska direktiv. Exemplet illustrerar en kömodell där en uppgift tas bort och bearbetas. Om du vill skydda dig mot många trådar som tar bort samma uppgift måste dequeuing-åtgärden finnas i ett critical avsnitt. Eftersom de två köerna i det här exemplet är oberoende skyddas de av critical direktiv med olika namn, xaxis och yaxis.
#pragma omp parallel shared(x, y) private(x_next, y_next)
{
#pragma omp critical ( xaxis )
x_next = dequeue(x);
work(x_next);
#pragma omp critical ( yaxis )
y_next = dequeue(y);
work(y_next);
}
A.6 Den sista privata satsen
Rätt genomförande beror på det värde som sista iterationen av en loop tilldelar en variabel. Sådana program måste lista alla sådana variabler som argument till en lastprivate-sats så att värdena för variablerna är desamma som när loopen körs sekventiellt.
#pragma omp parallel
{
#pragma omp for lastprivate(i)
for (i=0; i<n-1; i++)
a[i] = b[i] + b[i+1];
}
a[i]=b[i];
I föregående exempel är värdet i för i slutet av den parallella regionen lika med n-1, som i sekventiellt fall.
A.7 Reduktionsklausulen
I följande exempel visas reduktionssatsen:
#pragma omp parallel for private(i) shared(x, y, n) \
reduction(+: a, b)
for (i=0; i<n; i++) {
a = a + x[i];
b = b + y[i];
}
A.8 Parallella avsnitt
I följande exempel (för avsnitt 2.4.2) kan funktioner xaxis, yaxis och zaxi köras samtidigt. Det första section direktivet är valfritt. Alla section direktiv måste anges i konstruktionens parallel sections lexikala omfattning.
#pragma omp parallel sections
{
#pragma omp section
xaxis();
#pragma omp section
yaxis();
#pragma omp section
zaxis();
}
A.9 Enskilda direktiv
I följande exempel visas det enskilda direktivet. I exemplet skriver bara en tråd (vanligtvis den första tråden single som stöter på direktivet) ut förloppsmeddelandet. Användaren får inte göra några antaganden om vilken tråd som ska köra single avsnittet. Alla andra trådar hoppar över single avsnittet och stannar vid barriären i slutet av konstruktionen single . Om andra trådar kan fortsätta utan att vänta på att tråden som exekverar single-avsnittet, kan nowait-satsen anges på single-direktivet.
#pragma omp parallel
{
#pragma omp single
printf_s("Beginning work1.\n");
work1();
#pragma omp single
printf_s("Finishing work1.\n");
#pragma omp single nowait
printf_s("Finished work1 and beginning work2.\n");
work2();
}
A.10 Sekventiell ordning
Ordnade avsnitt är användbara för sekventiell ordning av utdata från arbete som utförs parallellt. Följande program skriver ut indexen i sekventiell ordning:
#pragma omp for ordered schedule(dynamic)
for (i=lb; i<ub; i+=st)
work(i);
void work(int k)
{
#pragma omp ordered
printf_s(" %d", k);
}
A.11 Ett fast antal trådar
Vissa program förlitar sig på ett fast, fördefinierat antal trådar för att köras korrekt. Eftersom standardinställningen för dynamisk justering av antalet trådar är implementeringsdefinierad kan sådana program välja att inaktivera funktionen för dynamiska trådar och uttryckligen ange antalet trådar för att behålla portabiliteten. I följande exempel visas hur du gör detta med hjälp av omp_set_dynamic och omp_set_num_threads:
omp_set_dynamic(0);
omp_set_num_threads(16);
#pragma omp parallel shared(x, npoints) private(iam, ipoints)
{
if (omp_get_num_threads() != 16)
abort();
iam = omp_get_thread_num();
ipoints = npoints/16;
do_by_16(x, iam, ipoints);
}
I det här exemplet körs programmet bara korrekt om det körs av 16 trådar. Om implementeringen inte kan stödja 16 trådar är beteendet för det här exemplet implementeringsdefinierat.
Antalet trådar som kör en parallell region förblir konstant under en parallell region, oavsett inställningen för dynamiska trådar. Mekanismen för dynamiska trådar avgör antalet trådar som ska användas i början av den parallella regionen och håller den konstant under hela regionen.
A.12 Atomdirektivet
I följande exempel undviks konkurrensförhållanden (samtidiga uppdateringar av ett element i x av många trådar) med hjälp av atomdirektivet :
#pragma omp parallel for shared(x, y, index, n)
for (i=0; i<n; i++)
{
#pragma omp atomic
x[index[i]] += work1(i);
y[i] += work2(i);
}
Fördelen med att använda atomic direktivet i det här exemplet är att det tillåter att uppdateringar av två olika element i x sker parallellt. Om ett kritiskt direktiv används i stället körs alla uppdateringar av element i x seriellt (men inte i någon garanterad ordning).
Direktivet atomic gäller endast för C- eller C++-instruktionen omedelbart efter det. Därför uppdateras inte element av y atomiskt i det här exemplet.
A.13 Ett tömningsdirektiv med en lista
I följande exempel används flush direktivet för punkt-till-punkt-synkronisering av specifika objekt mellan trådpar:
int sync[NUMBER_OF_THREADS];
float work[NUMBER_OF_THREADS];
#pragma omp parallel private(iam,neighbor) shared(work,sync)
{
iam = omp_get_thread_num();
sync[iam] = 0;
#pragma omp barrier
// Do computation into my portion of work array
work[iam] = ...;
// Announce that I am done with my work
// The first flush ensures that my work is
// made visible before sync.
// The second flush ensures that sync is made visible.
#pragma omp flush(work)
sync[iam] = 1;
#pragma omp flush(sync)
// Wait for neighbor
neighbor = (iam>0 ? iam : omp_get_num_threads()) - 1;
while (sync[neighbor]==0)
{
#pragma omp flush(sync)
}
// Read neighbor's values of work array
... = work[neighbor];
}
A.14 Ett tömningsdirektiv utan en lista
Följande exempel (för avsnitt 2.6.5) skiljer de delade objekt som påverkas av ett flush direktiv utan lista från de delade objekt som inte påverkas:
// omp_flush_without_list.c
#include <omp.h>
int x, *p = &x;
void f1(int *q)
{
*q = 1;
#pragma omp flush
// x, p, and *q are flushed
// because they are shared and accessible
// q is not flushed because it is not shared.
}
void f2(int *q)
{
#pragma omp barrier
*q = 2;
#pragma omp barrier
// a barrier implies a flush
// x, p, and *q are flushed
// because they are shared and accessible
// q is not flushed because it is not shared.
}
int g(int n)
{
int i = 1, j, sum = 0;
*p = 1;
#pragma omp parallel reduction(+: sum) num_threads(10)
{
f1(&j);
// i, n and sum were not flushed
// because they were not accessible in f1
// j was flushed because it was accessible
sum += j;
f2(&j);
// i, n, and sum were not flushed
// because they were not accessible in f2
// j was flushed because it was accessible
sum += i + j + *p + n;
}
return sum;
}
int main()
{
}
A.15 Antalet trådar som används
Överväg följande felaktiga exempel (för avsnitt 3.1.2):
np = omp_get_num_threads(); // misplaced
#pragma omp parallel for schedule(static)
for (i=0; i<np; i++)
work(i);
Anropet omp_get_num_threads() returnerar 1 i serieavsnittet i koden, så np är alltid lika med 1 i föregående exempel. För att fastställa antalet trådar som ska sättas upp för den parallella regionen bör anropet göras i den parallella regionen.
I följande exempel visas hur du skriver om det här programmet utan att inkludera en fråga om antalet trådar:
#pragma omp parallel private(i)
{
i = omp_get_thread_num();
work(i);
}
A.16 Lås
I följande exempel (för avsnitt 3.2) ska argumentet till låsfunktionerna ha typen omp_lock_t, och att det inte finns något behov av att tömma det. Låsfunktionerna gör att trådarna är inaktiva i väntan på att komma in i den första kritiska sektionen, men utför annat arbete medan de väntar på att komma in i den andra. Funktionen omp_set_lock blockerar, men omp_test_lock funktionen gör det inte, vilket möjliggör att arbetet i skip() kan utföras.
// omp_using_locks.c
// compile with: /openmp /c
#include <stdio.h>
#include <omp.h>
void work(int);
void skip(int);
int main() {
omp_lock_t lck;
int id;
omp_init_lock(&lck);
#pragma omp parallel shared(lck) private(id)
{
id = omp_get_thread_num();
omp_set_lock(&lck);
printf_s("My thread id is %d.\n", id);
// only one thread at a time can execute this printf
omp_unset_lock(&lck);
while (! omp_test_lock(&lck)) {
skip(id); // we do not yet have the lock,
// so we must do something else
}
work(id); // we now have the lock
// and can do the work
omp_unset_lock(&lck);
}
omp_destroy_lock(&lck);
}
A.17 Kapslade lås
Följande exempel (för avsnitt 3.2) visar hur ett kapslat lås kan användas för att synkronisera uppdateringar både till en hel struktur och till en av dess medlemmar.
#include <omp.h>
typedef struct {int a,b; omp_nest_lock_t lck;} pair;
void incr_a(pair *p, int a)
{
// Called only from incr_pair, no need to lock.
p->a += a;
}
void incr_b(pair *p, int b)
{
// Called both from incr_pair and elsewhere,
// so need a nestable lock.
omp_set_nest_lock(&p->lck);
p->b += b;
omp_unset_nest_lock(&p->lck);
}
void incr_pair(pair *p, int a, int b)
{
omp_set_nest_lock(&p->lck);
incr_a(p, a);
incr_b(p, b);
omp_unset_nest_lock(&p->lck);
}
void f(pair *p)
{
extern int work1(), work2(), work3();
#pragma omp parallel sections
{
#pragma omp section
incr_pair(p, work1(), work2());
#pragma omp section
incr_b(p, work3());
}
}
A.18 Kapslad för direktiv
Följande exempel på fordirektivkapsling är kompatibelt eftersom de inre och yttre for direktiven binder till olika parallella regioner:
#pragma omp parallel default(shared)
{
#pragma omp for
for (i=0; i<n; i++)
{
#pragma omp parallel shared(i, n)
{
#pragma omp for
for (j=0; j<n; j++)
work(i, j);
}
}
}
Följande variant av föregående exempel är också kompatibel:
#pragma omp parallel default(shared)
{
#pragma omp for
for (i=0; i<n; i++)
work1(i, n);
}
void work1(int i, int n)
{
int j;
#pragma omp parallel default(shared)
{
#pragma omp for
for (j=0; j<n; j++)
work2(i, j);
}
return;
}
A.19 Exempel som visar felaktig kapsling av arbetsdelningsdirektiv
Exemplen i det här avsnittet illustrerar direktivets kapslingsregler .
Följande exempel är inkompatibelt eftersom de inre och yttre for direktiven är kapslade och binder till samma parallel direktiv:
void wrong1(int n)
{
#pragma omp parallel default(shared)
{
int i, j;
#pragma omp for
for (i=0; i<n; i++) {
#pragma omp for
for (j=0; j<n; j++)
work(i, j);
}
}
}
Följande dynamiskt kapslade version av föregående exempel är också inkompatibel:
void wrong2(int n)
{
#pragma omp parallel default(shared)
{
int i;
#pragma omp for
for (i=0; i<n; i++)
work1(i, n);
}
}
void work1(int i, int n)
{
int j;
#pragma omp for
for (j=0; j<n; j++)
work2(i, j);
}
Följande exempel är inkompatibelt eftersom direktiven for och single är kapslade och de binder till samma parallella region:
void wrong3(int n)
{
#pragma omp parallel default(shared)
{
int i;
#pragma omp for
for (i=0; i<n; i++) {
#pragma omp single
work(i);
}
}
}
Följande exempel är inkompatibelt eftersom ett barrier direktiv i ett for kan leda till dödläge:
void wrong4(int n)
{
#pragma omp parallel default(shared)
{
int i;
#pragma omp for
for (i=0; i<n; i++) {
work1(i);
#pragma omp barrier
work2(i);
}
}
}
Följande exempel överensstämmer inte eftersom barrier resulterar i ett dödläge, eftersom endast en tråd i taget kan ange den kritiska sektionen.
void wrong5()
{
#pragma omp parallel
{
#pragma omp critical
{
work1();
#pragma omp barrier
work2();
}
}
}
Följande exempel är inkompatibelt eftersom barrier det resulterar i ett dödläge på grund av att endast en tråd kör single avsnittet:
void wrong6()
{
#pragma omp parallel
{
setup();
#pragma omp single
{
work1();
#pragma omp barrier
work2();
}
finish();
}
}
A.20 Bindningsbarriärdirektiv
Direktivets bindande regler kräver ett barrier direktiv som ska bindas till det närmaste omslutande parallel direktivet. Mer information om direktivbindning finns i avsnitt 2.8.
I följande exempel är anropet från main till sub2 kompatibelt eftersom barrier (i under3) binder till den parallella regionen i under2. Anropet från main till sub1 är kompatibelt eftersom barrier binder till den parallella regionen i subrutin sub2. Anropet från main till sub3 är kompatibelt eftersom barrier det inte binder till någon parallell region och ignoreras. Dessutom synkroniserar det barrier endast teamet av trådar i den omslutande parallella regionen och inte alla trådar som skapats i sub1.
int main()
{
sub1(2);
sub2(2);
sub3(2);
}
void sub1(int n)
{
int i;
#pragma omp parallel private(i) shared(n)
{
#pragma omp for
for (i=0; i<n; i++)
sub2(i);
}
}
void sub2(int k)
{
#pragma omp parallel shared(k)
sub3(k);
}
void sub3(int n)
{
work(n);
#pragma omp barrier
work(n);
}
A.21 Scope-variabler med privatklausulen
Värdena i för och j i följande exempel är odefinierade vid avslut från den parallella regionen:
int i, j;
i = 1;
j = 2;
#pragma omp parallel private(i) firstprivate(j)
{
i = 3;
j = j + 2;
}
printf_s("%d %d\n", i, j);
Mer information om satsen finns i privateavsnitt 2.7.2.1.
A.22 Standardsatsen (ingen)
I följande exempel särskiljs de variabler som påverkas av default(none) -satsen från variablerna som inte är det:
// openmp_using_clausedefault.c
// compile with: /openmp
#include <stdio.h>
#include <omp.h>
int x, y, z[1000];
#pragma omp threadprivate(x)
void fun(int a) {
const int c = 1;
int i = 0;
#pragma omp parallel default(none) private(a) shared(z)
{
int j = omp_get_num_thread();
//O.K. - j is declared within parallel region
a = z[j]; // O.K. - a is listed in private clause
// - z is listed in shared clause
x = c; // O.K. - x is threadprivate
// - c has const-qualified type
z[i] = y; // C3052 error - cannot reference i or y here
#pragma omp for firstprivate(y)
for (i=0; i<10 ; i++) {
z[i] = y; // O.K. - i is the loop control variable
// - y is listed in firstprivate clause
}
z[i] = y; // Error - cannot reference i or y here
}
}
Mer information om satsen finns i defaultavsnitt 2.7.2.5.
A.23 Exempel på det beställda direktivet
Det går att ha många ordnade avsnitt med en for angiven med ordered -satsen. Det första exemplet är inkompatibelt eftersom API:et anger följande regel:
"En iteration av en loop med en for konstruktion får inte köra samma ordered direktiv mer än en gång, och den får inte köra mer än ett ordered direktiv." (Se avsnitt 2.6.6.)
I det här inkompatibla exemplet kör alla iterationer två ordnade avsnitt:
#pragma omp for ordered
for (i=0; i<n; i++)
{
...
#pragma omp ordered
{ ... }
...
#pragma omp ordered
{ ... }
...
}
Följande exempel visar ett for som innehåller mer än en ordnad sektion.
#pragma omp for ordered
for (i=0; i<n; i++)
{
...
if (i <= 10)
{
...
#pragma omp ordered
{ ... }
}
...
(i > 10)
{
...
#pragma omp ordered
{ ... }
}
...
}
A.24 Exempel på den privata satsen
Den privata klausulen i en parallell region gäller endast för regionens lexikala omfattning, inte för regionens dynamiska omfattning. I exemplet nedan refererar därför alla användningar av variabeln a i loopen for i rutinen f till en privat kopia av en, medan en användning i rutin g refererar till global a.
int a;
void f(int n)
{
a = 0;
#pragma omp parallel for private(a)
for (int i=1; i<n; i++)
{
a = i;
g(i, n);
d(a); // Private copy of "a"
...
}
...
void g(int k, int n)
{
h(k,a); // The global "a", not the private "a" in f
}
A.25 Exempel på copyprivate-dataattributsatsen
Exempel 1:Copyprivate-satsen kan användas för att sända värden som hämtas av en enda tråd direkt till alla instanser av de privata variablerna i de andra trådarna.
float x, y;
#pragma omp threadprivate(x, y)
void init( )
{
float a;
float b;
#pragma omp single copyprivate(a,b,x,y)
{
get_values(a,b,x,y);
}
use_values(a, b, x, y);
}
Om rutinmässig init anropas från en seriell region påverkas inte dess beteende av förekomsten av direktiven. När anropet till get_values-rutinen har körts av en tråd lämnar ingen tråd konstruktionen förrän de privata objekt som anges av a, b, x och y i alla trådar har definierats med värdena lästa.
Exempel 2: Till skillnad från föregående exempel antar vi att läsningen måste utföras av en viss tråd, till exempel huvudtråden. I det här fallet copyprivate kan satsen inte användas för att göra sändningen direkt, men den kan användas för att ge åtkomst till ett tillfälligt delat objekt.
float read_next( )
{
float * tmp;
float return_val;
#pragma omp single copyprivate(tmp)
{
tmp = (float *) malloc(sizeof(float));
}
#pragma omp master
{
get_float( tmp );
}
#pragma omp barrier
return_val = *tmp;
#pragma omp barrier
#pragma omp single
{
free(tmp);
}
return return_val;
}
Exempel 3: Anta att antalet låsobjekt som krävs i en parallell region inte enkelt kan fastställas innan du anger det.
copyprivate Satsen kan användas för att ge åtkomst till delade låsobjekt som allokeras inom den parallella regionen.
#include <omp.h>
omp_lock_t *new_lock()
{
omp_lock_t *lock_ptr;
#pragma omp single copyprivate(lock_ptr)
{
lock_ptr = (omp_lock_t *) malloc(sizeof(omp_lock_t));
omp_init_lock( lock_ptr );
}
return lock_ptr;
}
A.26 Threadprivate-direktivet
Följande exempel visar hur du använder threadprivate-direktivet för att ge varje tråd en separat räknare.
Exempel 1
int counter = 0;
#pragma omp threadprivate(counter)
int sub()
{
counter++;
return(counter);
}
Exempel 2
int sub()
{
static int counter = 0;
#pragma omp threadprivate(counter)
counter++;
return(counter);
}
A.27 C99- variabellängdsmatriser
I följande exempel visas hur du använder C99 Variable Length Arrays (VLA) i ett firstprivate-direktiv .
Anmärkning
Matriser med variabellängd stöds för närvarande inte i Visual C++.
void f(int m, int C[m][m])
{
double v1[m];
...
#pragma omp parallel firstprivate(C, v1)
...
}
A.28 Klausulen num_threads
Följande exempel demonstrerar num_threads-klause. Den parallella regionen körs med högst 10 trådar.
#include <omp.h>
int main()
{
omp_set_dynamic(1);
...
#pragma omp parallel num_threads(10)
{
... parallel region ...
}
}
A.29 Arbetsdelningskonstruktioner i en kritisk konstruktion
I följande exempel visas hur du använder en arbetsdelningskonstruktion i en critical konstruktion. Det här exemplet är kompatibelt eftersom arbetsdelningskonstruktionen och konstruktionen critical inte binder till samma parallella region.
void f()
{
int i = 1;
#pragma omp parallel sections
{
#pragma omp section
{
#pragma omp critical (name)
{
#pragma omp parallel
{
#pragma omp single
{
i++;
}
}
}
}
}
}
A.30 Reprivatisering
I följande exempel visas reprivatiseringen av variabler. Privata variabler kan markeras private igen i ett kapslat direktiv. Du behöver inte dela dessa variabler i den omslutande parallella regionen.
int i, a;
...
#pragma omp parallel private(a)
{
...
#pragma omp parallel for private(a)
for (i=0; i<10; i++)
{
...
}
}
A.31 Trådsäkra låsfunktioner
I följande C++-exempel visas hur du initierar en matris med lås i en parallell region med hjälp av omp_init_lock.
// A_13_omp_init_lock.cpp
// compile with: /openmp
#include <omp.h>
omp_lock_t *new_locks() {
int i;
omp_lock_t *lock = new omp_lock_t[1000];
#pragma omp parallel for private(i)
for (i = 0 ; i < 1000 ; i++)
omp_init_lock(&lock[i]);
return lock;
}
int main () {}