A. Příklady

Následují příklady konstruktorů definovaných v tomto dokumentu. Příkaz za direktivou je složen pouze v případě potřeby a nekompilovaný příkaz je odsazený z předchozí direktivy.

Jednoduchá smyčka v paralelním provedení

Následující příklad ukazuje, jak paralelizovat smyčku pomocí direktivy parallel for. Proměnná iterace smyčky je ve výchozím nastavení soukromá, takže ji není nutné explicitně zadávat v privátní klauzuli.

#pragma omp parallel for
    for (i=1; i<n; i++)
        b[i] = (a[i] + a[i-1]) / 2.0;

Podmíněná kompilace A.2

Následující příklady ilustrují použití podmíněné kompilace pomocí makra OpenMP _OPENMP. Při kompilaci OpenMP se makro _OPENMP definuje.

# ifdef _OPENMP
    printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif

Definovaný operátor preprocesoru umožňuje testovat více než jedno makro v jedné direktivě.

# if defined(_OPENMP) && defined(VERBOSE)
    printf_s("Compiled by an OpenMP-compliant implementation.\n");
# endif

A.3 Paralelní oblasti

Paralelní direktivu lze použít v hrubých paralelních programech. V následujícím příkladu se každé vlákno v paralelní oblasti rozhodne, na jakou část globálního pole x má pracovat, na základě čísla vlákna:

#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 Klauzule nowait

Pokud existuje mnoho nezávislých smyček v rámci paralelní oblasti, můžete použít klauzuli nowait , abyste se vyhnuli implicitní bariérě na konci for direktivy, jak je znázorněno níže:

#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 Kritická direktiva

Následující příklad obsahuje několik kritických direktiv. Příklad znázorňuje model řazení do fronty, ve kterém je úkol vyřazen z fronty a poté se na něm pracuje. Pokud chcete chránit proti tomu, aby více vláken odebralo stejnou úlohu, musí být operace odřazení v oddílu critical. Vzhledem k tomu, že dvě fronty v tomto příkladu jsou nezávislé, jsou chráněny direktivami critical s různými názvy, xaxis a 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 Klauzule 'lastprivate'

Správné spuštění někdy závisí na hodnotě, kterou poslední iterace smyčky přiřadí proměnné. Tyto programy musí vypsat všechny takové proměnné jako argumenty do klauzule "lastprivate", aby hodnoty proměnných byly stejné jako při sekvenčním spuštění smyčky.

#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];

V předchozím příkladu se hodnota i na konci paralelní oblasti rovná n-1, jako v sekvenčním případě.

A.7 Klauzule redukce

Následující příklad ukazuje klauzuli redukce :

#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];
    }

Paralelní oddíly A.8

V následujícím příkladu (pro oddíl 2.4.2) je možné souběžně spouštět funkce xaxis, yaxis a zaxis . První section direktiva je nepovinná. Všechny section direktivy musí být uvedeny v lexikálním rozsahu parallel sections konstrukce.

#pragma omp parallel sections
{
    #pragma omp section
        xaxis();
    #pragma omp section
        yaxis();
    #pragma omp section
        zaxis();
}

Směrnice A.9 Jednoduché

Následující příklad ukazuje jedinou direktivu. V tomto příkladu vytiskne zprávu o průběhu pouze jedno vlákno (obvykle první vlákno, které narazí na direktivu single ). Uživatel nesmí provádět žádné předpoklady o tom, které vlákno provede single oddíl. Všechna ostatní vlákna přeskočí sekci single a zastaví se na bariéře na konci konstrukce single. Pokud mohou ostatní vlákna pokračovat, aniž by čekala na vlákno, které vykonává single oddíl, může být klauzule nowait specifikována v rámci direktivy single.

#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 Sekvenční řazení

Seřazené oddíly jsou užitečné pro postupné řazení výstupu z práce, která se provádí paralelně. Následující program vypíše indexy v sekvenčním pořadí:

#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 Pevný počet vláken

Některé programy spoléhají na pevný, předem zadaný počet vláken, aby se správně spustily. Vzhledem k tomu, že výchozí nastavení dynamické úpravy počtu vláken je definováno implementací, mohou se takové programy rozhodnout vypnout schopnost dynamických vláken a nastavit počet vláken explicitně pro zachování přenositelnosti. Následující příklad ukazuje, jak to provést pomocí omp_set_dynamic a 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);
}

V tomto příkladu se program správně provede pouze v případě, že je spuštěn na 16 vláknech. Pokud implementace nepodporuje 16 vláken, chování tohoto příkladu je definované implementací.

Počet vláken, která spouští paralelní oblast, zůstává konstantní během paralelní oblasti bez ohledu na nastavení dynamických vláken. Mechanismus dynamických vláken určuje počet vláken, která se mají použít na začátku paralelní oblasti, a udržuje ji konstantní po dobu trvání oblasti.

A.12 Atomická direktiva

Následující příklad zabraňuje kolizním stavům (současné aktualizace prvku x mnoha vlákny) pomocí direktivy atomic:

#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);
    }

Výhodou použití direktivy atomic v tomto příkladu je, že umožňuje paralelně provádět aktualizace dvou různých prvků x. Pokud se místo toho použije kritická direktiva, všechny aktualizace prvků x se spustí sériově (i když ne v žádném zaručené pořadí).

Direktiva atomic se vztahuje pouze na příkaz C nebo C++ bezprostředně za ním. V tomto příkladu se proto prvky y neaktualizují atomicky.

A.13 Příkaz flush se seznamem

Následující příklad používá direktivu flush pro synchronizaci konkrétních objektů mezi páry vláken:

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 Instrukce vyprázdnění bez seznamu

Následující příklad (pro oddíl 2.6.5) rozlišuje sdílené objekty ovlivněné direktivou flush bez seznamu ze sdílených objektů, které nejsou ovlivněny:

// 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 Počet použitých vláken

Představte si následující nesprávný příklad (v oddílu 3.1.2):

np = omp_get_num_threads(); // misplaced
#pragma omp parallel for schedule(static)
    for (i=0; i<np; i++)
        work(i);

Volání omp_get_num_threads() vrátí hodnotu 1 v sériové části kódu, takže np bude vždy rovna 1 v předchozím příkladu. K určení počtu vláken, která budou nasazena pro paralelní oblast, by volání mělo být uvnitř paralelní oblasti.

Následující příklad ukazuje, jak přepsat tento program bez zahrnutí dotazu na počet vláken:

#pragma omp parallel private(i)
{
    i = omp_get_thread_num();
    work(i);
}

Zámky A.16

V následujícím příkladu (pro oddíl 3.2) by měl argument funkcí zámku obsahovat typ omp_lock_ta že není nutné ho vyprázdnit. Funkce uzamčení způsobí, že vlákna budou nečinná při čekání na vstup do první kritické sekce, ale vykonávají jinou práci při čekání na vstup do druhé kritické sekce. Funkce omp_set_lock blokuje, ale funkce omp_test_lock ne, což umožňuje vykonat práci skip().

// 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 Vnořené zámky

Následující příklad (pro oddíl 3.2) ukazuje, jak lze použít vnořitelný zámek k synchronizaci aktualizací celé struktury i jednoho z jejích členů.

#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 Vnořené direktivy

Následující příklad vnoření direktiv je vyhovující, protože vnitřní a vnější direktivy se svazují s různými paralelními oblastmi.

#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);
            }
        }
}

Následující varianta předchozího příkladu je také kompatibilní:

#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 Příklady ukazující nesprávné vnoření direktiv sdílení práce

Příklady v této části ilustrují pravidla vnoření direktiv.

Následující příklad nedodržuje předpisy, protože vnitřní a vnější for direktivy jsou vnořené a svázané se stejnou parallel direktivou:

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);
     }
   }
}

Následující dynamicky vnořená verze předchozího příkladu také nedodržuje předpisy:

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);
}

Následující příklad není v souladu s předpisy, protože direktivy for a single jsou vnořené a svázané se stejnou paralelní oblastí.

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);
      }
  }
}

Následující příklad nedodržuje předpisy, protože direktiva barrier uvnitř for můžou vést k vzájemnému zablokování:

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);
      }
  }
}

Následující příklad je v rozporu s pravidly, protože barrier výsledkem je zablokování, protože v jednu chvíli může do kritické části vstoupit jen jedno vlákno:

void wrong5()
{
  #pragma omp parallel
  {
    #pragma omp critical
    {
       work1();
       #pragma omp barrier
       work2();
    }
  }
}

Následující příklad není v souladu, protože barrier vede ke zablokování způsobenému tím, že sekci single vykonává pouze jedno vlákno:

void wrong6()
{
  #pragma omp parallel
  {
    setup();
    #pragma omp single
    {
      work1();
      #pragma omp barrier
      work2();
    }
    finish();
  }
}

A.20 Direktivy pro vázání bariér

Pravidla pro vazbu direktiv vyžadují, aby direktiva barrier byla svázána s nejbližší obklopující direktivou parallel. Další informace o vazbě direktiv najdete v části 2.8.

V následujícím příkladu je volání z main do sub2 v souladu, protože barrier v sub3 je svázána s paralelní oblastí v sub2. Volání z main do sub1 je vyhovující, protože barrier je vázáno na paralelní oblast v podprogramu sub2. Volání z hlavní k pod3 je kompatibilní, protože barrier se neváže na žádnou paralelní oblast a ignoruje se. Kromě toho barrier synchronizuje pouze tým vláken v uzavřené paralelní oblasti a ne všechny vlákna vytvořená v pod1.

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 Proměnné oboru s privátní klauzulí

Hodnoty i a j v následujícím příkladu nejsou definovány při ukončení z paralelní oblasti:

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);

Další informace o klauzuli najdete v oddílu private 2.7.2.1.

A.22 Klauzule default(none)

Následující příklad rozlišuje proměnné, které jsou ovlivněny default(none) klauzulí, od proměnných, které nejsou:

// 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
   }
}

Další informace o klauzuli najdete v oddílu default 2.7.2.5.

A.23 Příklady uspořádaných direktiv

Je možné mít mnoho uspořádaných oddílů se for zadaným klauzulí ordered . První příklad nedodržuje předpisy, protože rozhraní API určuje následující pravidlo:

"Iterace smyčky s konstruktorem for nesmí spustit stejnou ordered direktivu více než jednou a nesmí spustit více než jednu ordered direktivu." (Viz bod 2.6.6.)

V tomto nevyhovujícím příkladu všechny iterace provádějí dvě uspořádané části:

#pragma omp for ordered
for (i=0; i<n; i++)
{
    ...
    #pragma omp ordered
    { ... }
    ...
    #pragma omp ordered
    { ... }
    ...
}

Následující příklad splňující požadavky for ukazuje více než jednu uspořádanou sekci:

#pragma omp for ordered
for (i=0; i<n; i++)
{
    ...
    if (i <= 10)
    {
        ...
        #pragma omp ordered
        { ... }
    }
    ...
    (i > 10)
    {
        ...
        #pragma omp ordered
        { ... }
    }
    ...
}

A.24 Příklad privátní klauzule

Privátní klauzule paralelní oblasti je platná pouze pro lexikální rozsah oblasti, nikoli pro dynamický rozsah oblasti. V následujícím příkladu tedy jakékoli použití proměnné a uvnitř smyčky v rutině f odkazuje na soukromou kopii a, zatímco použití v rutině g odkazuje na globální 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 Příklady klauzule atributu údaje copyprivate

Příklad 1: Klauzule copyprivate lze použít k vysílání hodnot získaných jedním vláknem přímo do všech instancí privátních proměnných v ostatních vláknech.

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);
}

Pokud je rutinní init volána ze sériové oblasti, její chování není ovlivněno přítomností direktiv. Po provedení volání rutiny get_values jedním vláknem, žádné vlákno neopustí konstrukci, dokud nejsou soukromé objekty určené a, b, x a y ve všech vláknech definovány s přečtenými hodnotami.

Příklad 2: Na rozdíl od předchozího příkladu předpokládejme, že čtení musí provést konkrétní vlákno, řekněme hlavní vlákno. V tomto případě copyprivate se klauzule nedá použít k přímému vysílání, ale dá se použít k poskytnutí přístupu k dočasnému sdílenému objektu.

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;
}

Příklad 3: Předpokládejme, že počet objektů zámků požadovaných v rámci paralelní oblasti nelze snadno určit před jeho zadáním. Klauzuli copyprivate lze použít k poskytnutí přístupu ke sdíleným objektům uzamčení, které jsou přiděleny v rámci této paralelní oblasti.

#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 Direktiva threadprivate

Následující příklady ukazují, jak pomocí direktivy threadprivate dát každému vláknu samostatný čítač.

Příklad 1

int counter = 0;
#pragma omp threadprivate(counter)

int sub()
{
    counter++;
    return(counter);
}

Příklad 2

int sub()
{
    static int counter = 0;
    #pragma omp threadprivate(counter)
    counter++;
    return(counter);
}

A.27 C99 pole s proměnnou délkou

Následující příklad ukazuje použití C99 Variable Length Arrays (VLAs) v direktivě firstprivate .

Poznámka:

Pole proměnné délky nejsou aktuálně podporována v jazyce Visual C++.

void f(int m, int C[m][m])
{
    double v1[m];
    ...
    #pragma omp parallel firstprivate(C, v1)
    ...
}

A.28 Klauzule num_threads

Následující příklad ukazuje klauzuli num_threads . Paralelní oblast se vykonává s maximálně 10 vlákny.

#include <omp.h>
int main()
{
    omp_set_dynamic(1);
    ...
    #pragma omp parallel num_threads(10)
    {
        ... parallel region ...
    }
}

A.29 Konstrukce sdílení práce uvnitř kritické konstrukce

Následující příklad ukazuje použití konstruktoru sdílení práce uvnitř konstruktoru critical . Tento příklad je kompatibilní, protože konstruktor sdílení práce a critical konstruktor není vázán na stejnou paralelní oblast.

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 Reprivatizace

Následující příklad ukazuje reprivatizaci proměnných. Privátní proměnné lze znovu označit private ve vnořené direktivě. Tyto proměnné nemusíte sdílet v uzavřené paralelní oblasti.

int i, a;
...
#pragma omp parallel private(a)
{
  ...
  #pragma omp parallel for private(a)
  for (i=0; i<10; i++)
     {
       ...
     }
}

A.31 Funkce zámku s podporou pro bezpečný přístup z více vláken

Následující příklad jazyka C++ ukazuje, jak inicializovat pole zámků v paralelní oblasti pomocí 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 () {}