Sdílet prostřednictvím


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.

A.1 Jednoduchá smyčka paralelně

Následující příklad ukazuje, jak paralelizovat smyčku pomocí paralelizace direktivy. 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 OpenMP se makro 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 pracuje na tom. Pokud chcete chránit před mnoha vlákny, které odřadí stejnou úlohu, musí být operace vyřazení z fronty 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 postupné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();
}

Direktivy A.9 Single

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čí single oddíl a zastaví se u bariéry na konci single konstrukce. Pokud mohou pokračovat další vlákna bez čekání na vlákno, které single spouští oddíl, nowait lze v direktivě single zadat klauzuli.

#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 program provede správně pouze v případě, že je spuštěn 16 vláken. 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 časování podmínek (souběžné aktualizace prvku x více vláken) pomocí atomické direktivy:

#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 Direktiva 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 Direktiva flush 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é části, ale při čekání na vstup na druhý provede další práci. Funkce omp_set_lock blokuje, ale omp_test_lock funkce neumožňuje provádět 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řený zámek k synchronizaci aktualizací celé struktury i jednoho ze svý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í for direktiv je kompatibilní, protože vnitřní a vnější for direktivy se sváže 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 nedodržuje předpisy, protože direktivy for a single direktivy jsou vnořené a jsou svázány 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 nedodržuje předpisy, protože barrier výsledkem je zablokování z důvodu skutečnosti, že v jednu chvíli může do kritické části vstoupit pouze jedno vlákno:

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

Následující příklad nedodržuje single předpisy, protože barrier výsledkem je zablokování kvůli skutečnosti, že oddíl spouští pouze jedno vlákno:

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

A.20 Direktivy bind bariéry

Pravidla vazby direktiv volají direktivu barrier , která se má svázat s nejbližší uzavřenou direktivou parallel . Další informace o vazbě direktiv najdete v části 2.8.

V následujícím příkladu je volání z hlavní k pod2 kompatibilní, protože barrier (v pod3) je vázána na paralelní oblast v sub2. Volání z hlavního na pod1 je kompatibilní, protože barrier vazby k paralelní oblasti 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 seřazené direktivy

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 příkladu nedodržující předpisy 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 vyhovující předpisům for ukazuje více než jeden seřazený oddíl:

#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

Soukromá 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é uvnitř smyčky v rutině f odkazuje na soukromou kopii a zatímco použití v rutině g odkazuje na globální hodnotu a.for

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 data 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, jeho chování není ovlivněno přítomností direktiv. Po volání rutiny get_values byla provedena jedním vláknem, žádné vlákno neopustí konstruktor, dokud soukromé objekty určené a, b, x a y ve všech vláknech byly definovány s hodnotami přečtenými.

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

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

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

Poznámka:

Pole délky proměnných nejsou v současné době 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 provádí s maximálně 10 vlákny.

#include <omp.h>
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 Reprivatization

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 bezpečného pro 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 () {}