A。 例

このドキュメントで定義されているコンストラクトの例を以下に示します。 ディレクティブの後のステートメントは、必要な場合にのみ複合です。非複合ステートメントは、その前にあるディレクティブからインデントされています。

A.1 並列の単純ループ

次の例は、parallel for ディレクティブを使用してループを並列化する方法を示しています。 ループ反復変数は既定でプライベートであるため、private 句で明示的に指定する必要はありません。

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

A.2 条件付きコンパイル

次の例は、OpenMP マクロ _OPENMP を使用した条件付きコンパイルの使用方法を示しています。 OpenMP コンパイルにより、_OPENMP マクロが定義されます。

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

定義されたプリプロセッサ演算子を使用すると、1 つのディレクティブで複数のマクロをテストできます。

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

A.3 並列領域

parallel ディレクティブは、粗粒度の並列プログラムで使用できます。 次の例では、並列領域の各スレッドは、スレッド番号に基づいて、グローバル配列 x のどの部分を処理するかを決定します。

#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 句

並列領域内に多数の独立したループがある場合は、次のように、nowait 句を使用することで、for ディレクティブの最後にある暗黙のバリアを回避できます。

#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 重大なディレクティブ

次の例には、critical ディレクティブがいくつか含まれています。 この例は、タスクがデキューされて処理されるキュー モデルを示しています。 多数のスレッドが同じタスクをデキューするのを防ぐために、デキュー操作は critical セクションに配置する必要があります。 この例の 2 つのキューは独立しているため、異なる名前 (critical と 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);
}

最後のprivate節

正しい実行が、ループの最後の反復で変数に割り当てる値に左右される場合があります。 このようなプログラムでは、変数の値が、ループを順次実行したときと同じになるように、このような変数をすべて lastprivate 句の引数として記載する必要があります。

#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 の値は、順次の場合と同様に、n-1 になります。

A.7 リダクション句

次の例は、リダクション句を示しています。

#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 並列セクション

次の例 (セクション 2.4.2 に対応) では、関数 xaxis、yaxis、zaxis を同時に実行できます。 最初の section ディレクティブは省略可能です。 すべての section ディレクティブは、parallel sections コンストラクトの字句エクステントに出現する必要があります。

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

A.9 単一ディレクティブ

次の例は、single ディレクティブを示しています。 この例では、1 つのスレッド (通常は single ディレクティブに最初に遭遇したスレッド) だけが進行状況メッセージを出力します。 どのスレッドが single セクションを実行するかについて、ユーザーが何かを想定することはできません。 他のすべてのスレッドは single セクションをスキップし、single コンストラクトの最後にあるバリアで停止します。 single セクションを実行するスレッドを待たずに、他のスレッドが続行できる場合は、nowait ディレクティブで 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 順序付け

整列セクションは、並列で実行された処理からの出力を順序通りに並べる際に役立ちます。 次のプログラムでは、インデックスが順番に出力されます。

#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 固定数のスレッド

正しく実行するために、あらかじめ指定された固定数のスレッドに依存するプログラムもあります。 スレッド数の動的調整の既定の設定は実装で定義されるため、このようなプログラムでは、動的スレッド機能を無効にし、スレッド数を明示的に設定して、移植性を維持することもできます。 次の例は、omp_set_dynamic と 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);
}

この例では、プログラムは 16 スレッドで実行された場合にのみ正しく実行されます。 実装が 16 スレッドをサポートできない場合、この例の動作は実装で定義されます。

並列領域を実行するスレッドの数は、動的スレッドの設定に関係なく、並列領域の間、一定に保たれます。 動的スレッド機構により、並列領域の開始時に使用するスレッド数が決定され、その領域の実行中は一定に保たれます。

A.12 アトミックディレクティブ

次の例では、atomic ディレクティブを使用して、競合状態 (複数のスレッドによる x の要素の同時更新) を回避しています。

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

この例で atomic ディレクティブを使用する利点は、x の 2 つの異なる要素の更新を並列で実行できることです。 代わりに critical ディレクティブを使用すると、x の要素に対するすべての更新が順次実行されます (ただし、順序は保証されません)。

atomic ディレクティブは、その直後の C または C++ ステートメントにのみ適用されます。 そのため、この例では y の要素はアトミックに更新されません。

A.13 リストを指定したフラッシュ・ディレクティブ

次の例では、スレッドのペア間で特定のオブジェクトのポイント ツー ポイント同期を行うために、flush ディレクティブを使用しています。

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 リストを指定しない flush ディレクティブ

次の例 (セクション 2.6.5 に対応) では、リストのない flush ディレクティブの影響を受ける共有オブジェクトと影響を受けない共有オブジェクトを区別しています。

// 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 使用するスレッドの数

次の誤った例を考えてみましょう (セクション 3.1.2 に対応)。

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

omp_get_num_threads() 呼び出しは、コードのシリアル セクションで 1 を返すので、前の例では np は常に 1 になります。 並列領域に配置されるスレッドの数を決定するには、この呼び出しを並列領域内に配置する必要があります。

次の例は、スレッド数のクエリを含めずにこのプログラムを書き換える方法を示しています。

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

A.16 ロック

次の例 (セクション 3.2 に対応) では、ロック関数の引数は omp_lock_t 型であり、それをフラッシュする必要はありません。 ロック関数により、スレッドは、最初のクリティカル セクションへのエントリを待機している間はアイドル状態になりますが、2 番目のセクションへのエントリを待機している間は他の処理を実行します。 omp_set_lock 関数はブロックしますが、omp_test_lock 関数はブロックしないため、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 入れ子にできるロック

次の例 (セクション 3.2) は、入れ子可能なロックを使用して、構造体全体およびそのメンバーの一部に対する更新をどのように同期するかを示しています。

#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 入れ子になった for ディレクティブ

次の forディレクティブの入れ子の例は、内側と外側の for ディレクティブが異なる並列領域にバインドされているため、準拠しています。

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

前の例の次のバリエーションも準拠しています。

#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 work-sharing ディレクティブの間違った入れ子を示す例

このセクションの例は、ディレクティブの入れ子規則を示しています。

次の例は、内側と外側の for ディレクティブが入れ子になっており、同じ parallel ディレクティブにバインドされているため、準拠していません。

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

前記の例の動的にネストされた次のバージョンも準拠していません。

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

次の例は、for ディレクティブと single ディレクティブが入れ子になっており、同じ並列領域にバインドされているため、準拠していません。

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

次の例は、barrier 内の for ディレクティブによってデッドロックが発生する可能性があるため、準拠していません。

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

次の例は、クリティカル セクションには一度に 1 つのスレッドしか入ることができないことから、barrier によってデッドロックが発生するため、準拠していません。

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

次の例は、barrier セクションを実行するスレッドは 1 つに限られることことから、single によってデッドロックが発生するため、準拠していません。

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

A.20 バリアディレクティブのバインド

barrier ディレクティブは、囲んでいる最も近い parallel ディレクティブにバインドすることが、ディレクティブ バインディング規則で求められています。 ディレクティブ バインディングの詳細については、セクション 2.8 を参照してください。

次の例では、(sub3 内の) は barrier の並列領域にバインドされているため、main からの sub2 の呼び出しは準拠しています。 はサブルーチン sub2 の並列領域にバインドされているため、barrier からの sub1 の呼び出しは準拠しています。 はどの並列領域にもバインドされておらず、無視されるため、main からの barrier の呼び出しは準拠しています。 また、barrier は、囲んでいる並列領域内のスレッドのチームのみを同期します。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 private 句を使用したスコープ変数

次の例の i と j の値は、並列領域の終了時に未定義になります。

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

private 句の詳細については、セクション 2.7.2.1 を参照してください。

A.22 デフォルト(none) 句

次の例では、default(none) 句の影響を受ける変数と影響を受けない変数を区別しています。

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

default 句の詳細については、セクション 2.7.2.5 を参照してください。

A.23 順序付きディレクティブの例

for 句で指定された ordered には、多くの順序付けられたセクションを含めることができます。 API では次の規則が指定されているため、最初の例は準拠していません。

"for コンストラクトを使用したループの反復では、同じ ordered ディレクティブを複数回実行することはできません。また、複数の ordered ディレクティブを実行することはできません。" (セクション 2.6.6 を参照してください)。

この非準拠の例では、すべての反復で 2 つの順序付きセクションが実行されます。

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

次の準拠の例は、複数の ordered セクションを含む for を示しています。

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

A.24 private 句の例

並列領域のprivate句は、領域の動的範囲ではなく、字句範囲でのみ有効です。 したがって、次の例では、ルーチン f の for ループ内での変数 a の使用は、a のプライベート コピーを参照し、ルーチン g での使用は、グローバル 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 copyprivate データ属性句の例

例 1:copyprivate 句を使用して、1 つのスレッドによって取得された値を、他のスレッドのプライベート変数のすべてのインスタンスに直接ブロードキャストできます。

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

ルーチン init がシリアル領域から呼び出された場合、その動作はディレクティブの存在の影響を受けません。 get_values ルーチンの呼び出しが 1 つのスレッドによって実行された後、すべてのスレッドの a、b、x、y で指定されたプライベート オブジェクトが読み取られた値で定義されるまで、どのスレッドもコンストラクトから離れることはありません。

例 2: 前の例とは異なり、特定のスレッド (マスター スレッドなど) によって読み取りが実行される必要があるとします。 この場合、copyprivate 句を使用して、ブロードキャストを直接実行することはできませんが、一時的な共有オブジェクトへのアクセスを提供することは可能です。

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

例 3: 並列領域に入る前に、その領域内で必要なロック オブジェクトの数を簡単に決定できないとします。 copyprivate 句を使用して、その並列領域内で割り当てられている共有ロック オブジェクトへのアクセスを提供できます。

#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 ディレクティブを使用して、各スレッドに個別のカウンターを提供する方法を示しています。

例 1

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

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

例 2

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

A.27 C99 可変長配列

次の例は、firstprivate ディレクティブで C99 可変長配列 (VLA) を使用する方法を示しています。

注

可変長配列は、Visual C++ では現在サポートされていません。

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

A.28 num_threads の句

次の例は、num_threads 句を示しています。 並列領域は、最大 10 スレッドで実行されます。

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

A.29 クリティカルコンストラクト内のワークシェアリングコンストラクト

次の例は、critical コンストラクト内で work-sharing コンストラクトを使用する方法を示しています。 この例は、work-sharing コンストラクトと critical コンストラクトが同じ並列領域にバインドされていないため、準拠しています。

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 再プライベート化

次の例は、変数の再プライベート化を示しています。 プライベート変数は、入れ子になったディレクティブで再度 private としてマークできます。 囲んでいる並列領域内でこれらの変数を共有する必要はありません。

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

A.31 スレッドセーフなロック関数

次の C++ の例は、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 () {}