2. ディレクティブ

ディレクティブは、C および C++ 標準で定義されている #pragma ディレクティブに基づいています。 OpenMP C および C++ API をサポートするコンパイラには、すべての OpenMP コンパイラ ディレクティブをアクティブにして解釈可能にするためのコマンド ライン オプションが組み込まれます。

2.1 ディレクティブの書式

OpenMP ディレクティブの構文は、付録 C の文法によって正式に指定されており、非公式には以下のようになります。

#pragma omp directive-name  [clause[ [,] clause]...] new-line

各ディレクティブは #pragma omp で始まり、同じ名前を持つ他の (OpenMP 以外または OpenMP のベンダー拡張機能の) pragma ディレクティブと競合する可能性を減らします。 ディレクティブの残りの部分は、コンパイラ ディレクティブの C および C++ 標準の規則に従います。 特に # の前後には空白を使用できます。また、ディレクティブ内の単語を区切るために、空白を使用しなければならない場合もあります。 #pragma omp に続く前処理トークンは、マクロ置換の対象となります。

ディレクティブでは大文字と小文字が区別されます。 ディレクティブ内の句の出現順序は重要ではありません。 ディレクティブ上の句は、各句の説明に記載されている制限に従って、必要に応じて繰り返すことができます。 variable-list が が句内に出現する場合は、変数のみを指定する必要があります。 ディレクティブごとに 1 つの directive-name だけを指定できます。 たとえば、次のディレクティブは許可されません。

/* ERROR - multiple directive names not allowed */
#pragma omp parallel barrier

OpenMP ディレクティブは、構造化ブロックでなければならない、最大 1 つの後続ステートメントに適用されます。

2.2 条件付きコンパイル

マクロ名 _OPENMP は、OpenMP に準拠した実装によって 10 進定数 yyyymm として定義されます。これは承認された仕様の年と月になります。 このマクロは、#define または #undef 前処理ディレクティブの対象にしないでください。

#ifdef _OPENMP
iam = omp_get_thread_num() + index;
#endif

ベンダーが OpenMP の拡張機能を定義する場合は、追加の定義済みマクロを指定できます。

2.3 並列コンストラクト

次のディレクティブにより並列領域が定義されます。これは、多数のスレッドによって並列実行されるプログラム領域です。 このディレクティブは、並列実行を開始する基本コンストラクトです。

#pragma omp parallel [clause[ [, ]clause] ...] new-line   structured-block

""は次のいずれかです。

  • if(scalar-expression)
  • private(variable-list)
  • firstprivate(variable-list)
  • default(shared | none)
  • shared(variable-list)
  • copyin(variable-list)
  • reduction(operator:variable-list)
  • num_threads(integer-expression)

スレッドが並列コンストラクトにアクセスし、次のいずれかが該当すると、スレッドのチームが作成されます。

  • if 句が存在しない。
  • if 式が 0 以外の値に評価される。

このスレッドはチームのマスター スレッドとなり、スレッド番号は 0 になります。領域は、マスター スレッドを含むチーム内のすべてのスレッドによって並列実行されます。 if の値が 0 の場合、領域はシリアル化されます。

要求されたスレッド数を確認するために、次の規則が順番に考慮されます。 条件が満たされた最初の規則が適用されます。

  1. num_threads 句が存在する場合、整数式の値が、要求されたスレッドの数である。

  2. omp_set_num_threads ライブラリ関数が呼び出された場合、直前に実行された呼び出しの引数の値が、要求されたスレッドの数である。

  3. 環境変数 OMP_NUM_THREADS が定義されている場合、この環境変数の値が、要求されたスレッド数である。

  4. 上記のいずれの方法も使用しない場合、要求されたスレッドの数が、実装で定義される。

num_threads 句が存在する場合、その句は、それが適用される並列領域についてのみ、omp_set_num_threads ライブラリ関数や OMP_NUM_THREADS 環境変数によって要求されたスレッドの数よりも優先されます。 以降の並列領域は、その影響を受けません。

また、並列領域を実行するスレッドの数は、スレッド数の動的調整が有効になっているかどうかによっても異なります。 動的調整が無効になっている場合、並列領域は、要求された数のスレッドによって実行されます。 動的調整が有効になっている場合、要求されたスレッド数は、並列領域を実行できる最大スレッド数になります。

スレッド数の動的調整が無効になっているときに並列領域が検出された場合、その並列領域に対して要求されたスレッド数が、ランタイム システムが提供できる数を超えるときは、プログラムの動作は実装で定義されます。 たとえば、実装によってプログラムの実行が中断されたり、並列領域がシリアル化されたりすることがあります。

omp_set_dynamic ライブラリ関数と OMP_DYNAMIC 環境変数を使用すると、スレッド数の動的調整を有効または無効にできます。

任意の時点でスレッドを実際にホストする物理プロセッサの数は、実装で定義されます。 チーム内で一度作成されたスレッドの数は、その並列領域の期間は一定に保たれます。 これは並列領域間でユーザーが明示的に変更することも、ランタイム システムによって自動的に変更することも可能です。

並列領域の動的エクステントに含まれるステートメントは、各スレッドによって実行されます。各スレッドは、他のスレッドとは異なるステートメントのパスを実行することができます。 並列領域の字句エクステント外で検出されたディレクティブは、孤立したディレクティブと呼ばれます。

並列領域の最後には暗黙のバリアがあります。 並列領域の最後では、チームのマスター スレッドのみが実行し続けます。

並列領域を実行しているチーム内のスレッドが、別の並列コンストラクトを検出すると、そのスレッドは新しいチームを作成し、その新しいチームのマスターになります。 入れ子になった並列領域は既定でシリアル化されます。 その結果、既定では、入れ子になった並列領域は、1 つのスレッドで構成されたチームによって実行されます。 この既定の動作を変更するには、ランタイム ライブラリ関数 omp_set_nested または環境変数 OMP_NESTED を使用します。 ただし、入れ子になった並列領域を実行するチーム内のスレッドの数は、実装で定義されます。

parallel ディレクティブに対する制限は次のとおりです。

  • ディレクティブ上に出現できる if 句は最大で 1 つです。

  • if 式または num_threads 式の中で副作用が発生するかどうかは特定できません。

  • 並列領域内で実行された throw は、同じ構造化ブロックの動的エクステント内で実行を再開させる必要があります。また、例外をスローしたスレッドでこれをキャッチする必要があります。

  • ディレクティブ上に出現できる num_threads 句は 1 つだけです。 num_threads 式は並列領域のコンテキスト外で評価されます。また、正の整数値に評価されなければなりません。

  • if 句と num_threads 句の評価の順序は指定されていません。

クロスリファレンス

2.4 動作共有のコンストラクト

動作共有のコンストラクトは、関連するステートメントの実行を、それを検出したチームのメンバーに分散します。 動作共有のディレクティブが新しいスレッドを起動することはありません。また、動作共有のコンストラクトのエントリ上に暗黙のバリアはありません。

検出された動作共有のコンストラクトと barrier ディレクティブのシーケンスは、チーム内のすべてのスレッドで同じである必要があります。

OpenMP では、次の動作共有のコンストラクトが定義されています。これらのコンストラクトについては、後のセクションで説明します。

  • for ディレクティブ
  • sections ディレクティブ
  • single ディレクティブ

2.4.1 for コンストラクト

for ディレクティブは、関連するループの反復が並列実行されることを指定する、反復的な動作共有のコンストラクトを特定します。 for ループの反復は、チーム内に既に存在し、そのループがバインドされている並列コンストラクトを実行しているスレッドに分散されます。 for コンストラクトの構文は次のとおりです。

#pragma omp for [clause[[,] clause] ... ] new-line for-loop

"句"は次のいずれかです。

  • private(variable-list)
  • firstprivate(variable-list)
  • lastprivate(variable-list)
  • reduction(operator:variable-list)
  • ordered
  • schedule(kind [,chunk_size] )
  • nowait

for ディレクティブによって、対応する for ループの構造に制限が設定されます。 具体的には、対応する for ループには正規のシェイプが必要です。

for (init-expr;var logical-op b;incr-expr)

init-expr
次のいずれか:

  • var = lb
  • integer-type var = lb

incr-expr
次のいずれか:

  • ++var
  • var++
  • --var
  • var--
  • var+=incr
  • var-=incr
  • var=var+incr
  • var=incr+var
  • var=var-incr

var
符号付き整数の変数。 また、この変数が共有されている場合、for の期間、これは暗黙的にプライベートになります。 for ステートメントの本文内で、この変数は変更しないでください。 変数に lastprivate が指定されていない限り、ループの後のその値は不確定です。

logical-op
次のいずれか:

  • <
  • <=
  • >
  • >=

lbbincr
ループ インバリアント整数式。 これらの式の評価中は同期が行われません。このため、評価された副作用によって不確定な結果がもたらされます。

正規の形式を使用すると、ループに入るときにループ反復の数を計算できます。 この計算は、整数の上位変換後、var 型の値を使用して行われます。 特に、b-lb+incr の値をその型で表すことができない場合、結果は不確定になります。 さらに、logical-op< または <= である場合、incr-expr は、ループの反復ごとに var を増やす必要があります。 logical-op> または >= である場合、incr-expr は、ループの反復ごとに var を小さくしなければなりません。

schedule 句は、for ループの反復が、チームのスレッド間にどのように分割されるかを指定します。 プログラムの正確性は、どのスレッドが特定の反復を実行するかに依存してはいけません。 chunk_size の値は (指定されている場合)、正の値を持つループ インバリアント整数式である必要があります。 この式の評価中は同期が行われません。このため、評価された副作用によって不確定な結果がもたらされます。 スケジュール kind には、以下のいずれかの値を指定できます。

表 2-1: schedulekind の値

Value 説明
静的 schedule(static,chunk_size) が指定されている場合、反復は chunk_size で指定されたサイズのチャンクに分割されます。 チャンクは、チーム内のスレッドに、ラウンド ロビン方式でスレッド番号順に静的に割り当てられます。 chunk_size が指定されていない場合、反復空間はほぼ同じサイズのチャンクに分割され、各スレッドに 1 つのチャンクが割り当てられます。
dynamic schedule(dynamic,chunk_size) が指定されている場合、反復は一連のチャンクに分割され、それぞれに chunk_size の反復が含まれています。 割り当てを待っているスレッドに各チャンクが割り当てられ、 スレッドは反復のチャンクを実行したら、次が割り当てられるのを待ちます。これは、割り当て対象の残りのチャンクがなくなるまで繰り返されます。 最後に割り当てられるチャンクは、反復数が少ない場合があります。 chunk_size が指定されていない場合、既定値は 1 です。
guided schedule(guided,chunk_size) が指定されている場合、反復は、サイズが小さくなるチャンクでスレッドに割り当てられます。 割り当てられた反復のチャンクが完了したスレッドには、次のチャンクが動的に割り当てられ、残りがなくなるまで繰り返されます。 chunk_size が 1 の場合、各チャンクのサイズは、未割り当ての反復の数を、スレッド数で割ったものに近い値になります。 これらのサイズは 1 に向かってほぼ指数関数的に減少します。 chunk_size が 1 より大きい値 k の場合、サイズはほぼ指数関数的に k まで減少しますが、最後のチャンクの反復は k 未満になる場合があります。 chunk_size が指定されていない場合、既定値は 1 です。
ランタイム schedule(runtime) が指定されている場合、スケジュールに関する決定はランタイムまで延期されます。 スケジュール kind とチャンクのサイズは、環境変数 OMP_SCHEDULE を設定することで、実行時に選択できます。 この環境変数が設定されていない場合、結果のスケジュールは実装で定義されます。 schedule(runtime) が指定されている場合は、chunk_size を指定しないでください。

明示的に定義されたschedule 句がない場合、既定値 schedule は実装で定義されます。

OpenMP 準拠のプログラムを正しく実行するには、特定のスケジュールに依存してはいけません。 スケジュール kind が同じでも、コンパイラによって実装が異なる可能性があるため、プログラムは、上記の説明に正確に準拠するスケジュール kind に依存するべきではありません。 この説明は、状況に適したスケジュールを選択するために、使用することができます。

ordered 句は、ordered ディレクティブが for コンストラクトにバインドされるとき、存在していなければなりません。

nowait 句が指定されていない限り、for コンストラクトの最後には暗黙のバリアがあります。

for ディレクティブに対する制限は次のとおりです。

  • for ループは構造化されたブロックある必要があります。また、その実行は break ステートメントで終了してはいけません。

  • for ディレクティブに関連付けられている for ループのループ コントロール式の値は、チーム内のすべてのスレッドで同じである必要があります。

  • for ループ反復変数には、符号付き整数型が必要です。

  • for ディレクティブ上に出現できる schedule 句は 1 つだけです。

  • for ディレクティブ上に出現できる ordered 句は 1 つだけです。

  • for ディレクティブ上に出現できる nowait 句は 1 つだけです。

  • chunk_sizelbbincr 式の中で副作用が発生するかどうか、また、どのくらいの頻度で発生するかは特定できません。

  • chunk_size 式の値は、チーム内のすべてのスレッドで同じである必要があります。

クロスリファレンス

2.4.2 sections コンストラクト

sections ディレクティブは、チーム内のスレッド間で分割される一連のコンストラクトを指定する、反復的な動作共有コンストラクト以外を特定します。 各セクションは、チーム内のスレッドによって 1 回実行されます。 sections ディレクティブの構文は次のとおりです。

#pragma omp sections [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block ]
...
}

"句"は次のいずれかです。

  • private(variable-list)
  • firstprivate(variable-list)
  • lastprivate(variable-list)
  • reduction(operator:variable-list)
  • nowait

各セクションの前に section ディレクティブがありますが、最初のセクションでは section ディレクティブは省略可能です。 section ディレクティブは、sections ディレクティブの字句エクステント内に出現する必要があります。 nowait が指定されていない限り、sections コンストラクトの最後には暗黙のバリアがあります。

sections ディレクティブに対する制限は次のとおりです。

  • section ディレクティブは、sections ディレクティブの字句エクステント外に出現することはできません。

  • sections ディレクティブ上に出現できる nowait 句は 1 つだけです。

クロスリファレンス

2.4.3 single コンストラクト

single ディレクティブは、関連する構造化ブロックがチーム内の 1 つのスレッド (マスター スレッドとは限りません) によってのみ実行されることを指定するコンストラクトを特定します。 single ディレクティブの構文は次のとおりです。

#pragma omp single [clause[[,] clause] ...] new-linestructured-block

"句"は次のいずれかです。

  • private(variable-list)
  • firstprivate(variable-list)
  • copyprivate(variable-list)
  • nowait

nowait 句が指定されていない限り、single コンストラクトの後ろには暗黙のバリアがあります。

single ディレクティブに対する制限は次のとおりです。

  • single ディレクティブ上に出現できる nowait 句は 1 つだけです。
  • copyprivate句は、nowait 句と共に使用することはできません。

クロスリファレンス

2.5 結合された並列動作共有のコンストラクト

結合された並列動作共有のコンストラクトは、動作共有のコンストラクトが 1 つしかない並列領域を指定するためのショートカットです。 これらのディレクティブのセマンティクスは、parallel ディレクティブの後に 1 つの動作共有コンストラクトを明示的に指定することと同じです。

以下のセクションでは、結合された並列動作共有のコンストラクトについて説明します。

2.5.1 parallel for コンストラクト

parallel for ディレクティブは、1 つの for ディレクティブのみを含む parallel 領域のショートカットです。 parallel for ディレクティブの構文は次のとおりです。

#pragma omp parallel for [clause[[,] clause] ...] new-linefor-loop

このディレクティブを使用すると、parallel ディレクティブと for ディレクティブのすべての句 (nowait 句を除く) が、同じ意味と制限を持つことができます。 セマンティクスは、parallel ディレクティブの直後に for ディレクティブを明示的に指定することと同じです。

クロスリファレンス

2.5.2 parallel sections コンストラクト

parallel sections ディレクティブは、1 つの sections ディレクティブのみを含む parallel 領域を指定するためのショートカット フォームを提供します。 セマンティクスは、parallel ディレクティブの直後に sections ディレクティブを明示的に指定することと同じです。 parallel sections ディレクティブの構文は次のとおりです。

#pragma omp parallel sections  [clause[[,] clause] ...] new-line
   {
   [#pragma omp section new-line]
      structured-block
   [#pragma omp section new-linestructured-block  ]
   ...
}

clause は、parallel ディレクティブと sections ディレクティブが受け入れる句のいずれかにできます (nowait 句を除く)。

クロスリファレンス

2.6 マスター ディレクティブと同期ディレクティブ

以下のセクションでは、次について説明します。

2.6.1 master コンストラクト

master ディレクティブは、チームのマスター スレッドによって実行される構造化ブロックを指定するコンストラクトを特定します。 master ディレクティブの構文は次のとおりです。

#pragma omp master new-linestructured-block

チーム内の他のスレッドでは、関連する構造化ブロックは実行されません。 master コンストラクトの開始時または終了時に暗黙のバリアはありません。

2.6.2 critical コンストラクト

critical ディレクティブは、関連する構造化ブロックの実行を一度に 1 つのスレッドに制限するコンストラクトを特定します。 critical ディレクティブの構文は次のとおりです。

#pragma omp critical [(name)]  new-linestructured-block

オプションの name を使用すると、クリティカル領域を特定できます。 クリティカル領域の特定に使用される識別子は、外部リンケージを持ち、ラベル、タグ、メンバー、通常の識別子によって使用される名前空間とは別の名前空間にあります。

スレッドは、クリティカル領域の先頭で、(プログラム内の任意の場所で) 同じ名前のクリティカル領域を実行するスレッドが他になくなるまで待機します。 名前のない critical ディレクティブはすべて、同じ未設定の名前にマップされます。

2.6.3 barrier ディレクティブ

barrier ディレクティブは、チーム内のすべてのスレッドを同期します。 検出されると、チーム内の各スレッドは、他のすべてのスレッドがこのポイントに到達するまで待機します。 barrier ディレクティブの構文は次のとおりです。

#pragma omp barrier new-line

チーム内のすべてのスレッドがバリアを検出すると、チーム内の各スレッドが、バリア ディレクティブの後ろにあるステートメントを並列で実行し始めます。 barrier ディレクティブには C 言語のステートメントがその構文の一部として含まれていないため、プログラム内に配置するには制限がいくつかあります。 正式な文法の詳細については、「付録 C」を参照してください。以下の例は、これらの制限を示しています。

/* 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 atomic コンストラクト

atomic ディレクティブは、特定のメモリ位置のアトミックな更新を、複数の同時書き込みスレッドの可能性にさらすことなく確実に実施します。 atomic ディレクティブの構文は次のとおりです。

#pragma omp atomic new-lineexpression-stmt

式ステートメントには、次のいずれかのフォームが必要です。

  • x binop=expr
  • x++
  • ++x
  • x--
  • --x

上記の式の各要素の意味は次のとおりです。

  • x は、スカラー型の lvalue 式です。

  • expr はスカラー型の式であり xによって指定されたオブジェクトを参照しません。

  • binop はオーバーロードされた演算子ではなく、+*-/&^|<<>> のいずれかです。

実装によってすべての atomic ディレクティブが、同じ一意の name を持つ critical ディレクティブに置き換えられるかどうかは、実装で定義されていますが、atomic ディレクティブを使用すると、より適切に最適化を行うことができます。 多くの場合、ハードウェア命令を使用でき、アトミックな更新は最小のオーバーヘッドで実行できます。

アトミックなのは x によって指定されたオブジェクトの読み込みと格納のみです。expr の評価はアトミックではありません。 競合状態を避けるには、並列で行われる場所の更新すべてを atomic ディレクティブで保護する必要があります。ただし、競合状態が発生しないことがわかっている更新は除きます。

atomic ディレクティブに対する制限は次のとおりです。

  • プログラム全体のストレージの場所 x に対するすべてのアトミック参照に、互換性のある型が必要です。

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 flush ディレクティブ

flush ディレクティブは、明示的か暗黙的かにかかわらず、"スレッド間" のシーケンス ポイントを指定します。チーム内のすべてのスレッドが、メモリ内の特定のオブジェクト (以下で指定) の一貫したビューを確実に持つには、この実装が必要です。 これは、これらのオブジェクトを参照する式に対する前の評価が完了し、後続の評価がまだ開始されていないことを意味します。 たとえば、コンパイラは、オブジェクトの値をレジスタからメモリに復元する必要があります。また、ハードウェアは、書き込みバッファーをメモリにフラッシュし、オブジェクトの値をメモリから再読み込みしなければならない場合があります。

flush ディレクティブの構文は次のとおりです。

#pragma omp flush [(variable-list)]  new-line

同期を必要とするオブジェクトすべてを変数によって指定できる場合、それらの変数は、省略可能な variable-list 内で指定できます。 variable-list 内にポインターが存在する場合は、そのポインター自体がフラッシュされます。ポインターが参照するオブジェクトではありません。

variable-list のない flush ディレクティブは、自動ストレージ期間が指定されているアクセスできないオブジェクト以外の、すべての共有オブジェクトを同期します (これは、変数リストを使用する場合よりもflushオーバーヘッドが多くなる可能性があります)。flush変数リストのないディレクティブは、次のディレクティブに対して暗黙的に指定されます。

  • barrier
  • critical の開始時と終了時
  • ordered の開始時と終了時
  • parallel の開始時と終了時
  • for の終了時
  • sections の終了時
  • single の終了時
  • parallel for の開始時と終了時
  • parallel sections の開始時と終了時

nowait 句が存在する場合、ディレクティブは暗黙的に指定されません。 flush ディレクティブは以下に対しても暗黙的に指定されなことに注意してください。

  • for の開始時
  • master の開始時または終了時
  • sections の開始時
  • single の開始時

volatile 修飾型のオブジェクトの値にアクセスする参照は、前のシーケンス ポイントでそのオブジェクトを指定する flush ディレクティブが存在するかのように動作します。 volatile 修飾型のオブジェクトの値を変更する参照は、後続のシーケンス ポイントでそのオブジェクトを指定する flush ディレクティブが存在するかのように動作します。

flush ディレクティブには C 言語のステートメントがその構文の一部として含まれていないため、プログラム内に配置するには制限がいくつかあります。 正式な文法の詳細については、「付録 C」を参照してください。以下の例は、これらの制限を示しています。

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

flush ディレクティブに対する制限は次のとおりです。

  • flush ディレクティブ内で指定された変数に、参照型を使用することはできません。

2.6.6 ordered コンストラクト

ordered ディレクティブに続く構造化ブロックは、順次ループ内で反復が実行される順序で実行されます。 ordered ディレクティブの構文は次のとおりです。

#pragma omp ordered new-linestructured-block

ordered ディレクティブは、for コンストラクトまたは parallel for コンストラクトの動的エクステント内になければなりません。 ordered コンストラクトがバインドされている または for ディレクティブまたは parallel for ディレクティブでは、セクション 2.4.1 で説明されているように、ordered 句が指定されていなければなりません。 ordered 句を指定して for コンストラクトまたは parallel for コンストラクトを実行する場合、ordered コンストラクトは、ループの順次実行で実行される順序で厳密に実行されます。

ordered ディレクティブに対する制限は次のとおりです。

  • for コンストラクトを使用したループの反復では、同じ ordered ディレクティブを複数回実行することはできません。また、複数の ordered ディレクティブを実行することはできません。

2.7 データ環境

このセクションでは、並列領域の実行中にデータ環境を制御するディレクティブと、複数の句について説明します。

  • threadprivate ディレクティブは、ファイル スコープ、名前空間スコープ、または静的ブロック スコープの変数を、スレッドに対してローカルにするために用意されています。

  • 並列コンストラクトまたは動作共有コンストラクトの期間、変数の共有属性を制御するためにディレクティブ上で指定できる句については、セクション 2.7.2 で説明します。

2.7.1 threadprivate ディレクティブ

threadprivate ディレクティブは、variable-list で指定された名前付きファイル スコープ、名前空間スコープ、または静的ブロック スコープの変数を、スレッドに対してプライベートにします。 variable-list は、不完全な型を持たない変数のコンマ区切りのリストです。 threadprivate ディレクティブの構文は次のとおりです。

#pragma omp threadprivate(variable-list) new-line

threadprivate 変数の各コピーは、そのコピーが最初に参照される前、プログラム内の未指定のポイントで、通常の方法で (つまり、プログラムのシリアル実行でマスター コピーが初期化されるように) 1 回初期化されます。 オブジェクトが threadprivate 変数の明示的な初期化子内で参照されている場合、そのオブジェクトの値が、その変数のコピーが最初に参照される前に変更されると、動作は指定されません。

他のプライベート変数と同様、スレッドが、threadprivate オブジェクトの他のスレッドのコピーを参照することはできません。 プログラムのシリアル領域とマスター領域の期間中は、オブジェクトのマスター スレッドのコピーが参照されます。

最初の並列領域が実行された後、threadprivate オブジェクト内のデータが保持されることが保証されるのは、動的スレッド メカニズムが無効で、すべての並列領域に対してスレッド数が変更されていない場合のみです。

threadprivate ディレクティブに対する制限は次のとおりです。

  • ファイル スコープまたは名前空間スコープの変数の threadprivate ディレクティブは、定義または宣言の外側に出現し、リスト内の変数へのすべての参照に字句的に先行しなければなりません。

  • ファイル スコープまたは名前空間スコープの threadprivate ディレクティブの variable-list 内にある各変数は、ファイル スコープまたは名前空間スコープで、ディレクティブに字句的に先行する変数宣言を参照する必要があります。

  • 静的ブロック スコープ変数の threadprivate ディレクティブは、入れ子になったスコープではなく、変数のスコープ内に出現する必要があります。 ディレクティブは、リスト内の変数へのすべての参照に字句的に先行する必要があります。

  • ブロック スコープ内の threadprivate ディレクティブの variable-list 内にある各変数は、同じスコープで、ディレクティブに字句的に先行する変数宣言を参照する必要があります。 変数宣言は、静的な storage-class 指定子を使用する必要があります。

  • ある翻訳単位内の threadprivate ディレクティブで変数が指定されている場合、その変数は、それが宣言されているすべての翻訳単位内の threadprivate ディレクティブで指定されている必要があります。

  • threadprivate 変数は、copyin 句、copyprivate 句、schedule 句、num_threads 句、if 句以外の句内に出現することはできません。

  • threadprivate 変数のアドレスはアドレス定数ではありません。

  • threadprivate 変数に、不完全な型や参照型を使用することはできません。

  • POD 以外のクラス型を持つ threadprivate 変数が明示的な初期化子で宣言されている場合、この変数には、アクセス可能で明確なコピー コンストラクターが必要です。

次の例は、初期化子に表示される変数を変更することで、不特定の動作がどのように発生するか、また、補助オブジェクトとコピー コンストラクターを使って、この問題をどのように回避するかを示しています。

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

クロスリファレンス

2.7.2 データ共有属性句

複数のディレクティブが、領域の期間中の変数の共有属性を、ユーザーが制御できるようにする句を受け入れます。 共有属性句は、その句が表示されるディレクティブの、字句エクステント内にある変数にのみ適用されます。 以下のすべての句が、すべてのディレクティブで許可されているわけではありません。 特定のディレクティブ上で有効な句のリストは、そのディレクティブに記載されています。

並列コンストラクトまたは動作共有コンストラクトが検出されたときに、変数が表示され、その変数が、共有属性句または threadprivate ディレクティブで指定されていない場合、その変数は共有されます。 並列領域の動的エクステント内で宣言された静的変数は共有されます。 ヒープ割り当てメモリ ( たとえば、C または C++ で malloc() を、C++ で new 演算子を使用) は共有されます (ただし、このメモリへのポインターはプライベートでも共有でもかまいません)。並列リージョンの動的エクステント内で宣言された自動ストレージ期間を持つ変数はプライベートです。

ほとんどの句が、表示される変数のコンマ区切りのリストである variable-list 引数を受け入れます。 データ共有属性句内で参照される変数に、テンプレートから派生した型が含まれ、プログラム内にその変数への他の参照がない場合、動作は定義されていません。

ディレクティブ句内に出現する変数はすべて、参照可能である必要があります。 句は必要に応じて繰り返すことができますが、複数の句で変数を指定することはできません。ただし、両方の句で変数を指定できる firstprivate 句と lastprivate 句を除きます。

以下のセクションでは、データ共有属性句について説明します。

2.7.2.1 private

private 句は、variable-list 内の変数を、チーム内の各スレッドに対してプライベートとして宣言します。 private 句の構文は次のとおりです。

private(variable-list)

private 句内で指定された変数の動作は次のとおりです。 自動ストレージ期間を持つ新しいオブジェクトが、コンストラクトに割り当てされます。 新しいオブジェクトのサイズと配置は、変数の型によって決まります。 この割り当ては、チーム内のスレッドごとに 1 回行われます。また、必要に応じて、クラス オブジェクトに対して既定のコンストラクターが呼び出されます。それ以外の場合、初期値は不確定です。 変数によって参照される、コンストラクトの開始時の元のオブジェクトの値は不確定で、コンストラクトの動的エクステント内で変更することはできません。また、コンストラクトの終了時の値も不確定です。

ディレクティブ コンストラクトの字句エクステント内で、変数は、スレッドによって割り当てられた新しいプライベート オブジェクトを参照します。

private 句に対する制限は次のとおりです。

  • private 句内で指定されたクラス型を持つ変数には、アクセス可能で明確な既定のコンストラクターが必要です。

  • mutable メンバーを持つクラス型がない限り、private 句内で指定された変数に const 修飾型を使用することはできません。

  • private 句内で指定された変数に、不完全な型または参照型を使用することはできません。

  • parallel ディレクティブの reduction 句に出現する変数を、並列コンストラクトにバインドされる動作共有ディレクティブ上の private 句内で指定することはできません。

2.7.2.2 firstprivate

firstprivate 句は、private 句によって提供された機能のスーパーセットを提供します。 firstprivate 句の構文は次のとおりです。

firstprivate(variable-list)

セクションsection 2.7.2.1 で説明されているように、variable-list 内で指定された変数には、private 句のセマンティクスがあります。 初期化やコンストラクションは、スレッドによるコンストラクトの実行前に、スレッドごとに 1 回行われたかのように発生します。 並列コンストラクト上の firstprivate 句の場合、新しいプライベート オブジェクトの初期値は、並列コンストラクトを検出したスレッドに対する、その並列コンストラクトの直前に存在する元のオブジェクトの値になります。 動作共有コンストラクト上の firstprivate 句の場合、動作共有コンストラクトを実行する各スレッドの新しいプライベート オブジェクトの初期値は、その同じスレッドが動作共有コンストラクトを検出した時点より前に存在する元のオブジェクトの値になります。 さらに、C++ オブジェクトの場合、各スレッドの新しいプライベート オブジェクトは、元のオブジェクトから構築されたコピーです。

firstprivate 句に対する制限は次のとおりです。

  • firstprivate 句内で指定された変数に、不完全な型または参照型を使用することはできません。

  • firstprivate として指定されたクラス型を持つ変数には、アクセス可能で明確なコピー コンストラクターが必要です。

  • 並列領域内でプライベートの変数、または parallel ディレクティブの reduction 句に出現する変数を、並列コンストラクトにバインドされる動作共有ディレクティブ上の firstprivate 句内で指定することはできません。

2.7.2.3 lastprivate

lastprivate 句は、private 句によって提供された機能のスーパーセットを提供します。 lastprivate 句の構文は次のとおりです。

lastprivate(variable-list)

variable-list で指定された変数には、private 句のセマンティクスがあります。 動作共有のコンストラクトを特定するディレクティブ上に lastprivate 句が出現すると、関連するループの最後の順次反復または字句的に最後のセクション ディレクティブの各 lastprivate 変数の値が、その変数の元のオブジェクトに割り当てられます。 forparallel for の最後の反復、または sections ディレクティブや parallel sections ディレクティブの字句的に最後のセクションによって値が割り当てられていない変数については、コンストラクトの後ろに不確定な値が設定されます。 未割り当てのサブオブジェクトでも、コンストラクトの後ろに不確定の値が設定されます。

lastprivate 句に対する制限は次のとおりです。

  • private に対するすべての制限が適用されます。

  • lastprivate として指定されたクラス型を持つ変数には、アクセス可能で明確なコピー代入演算子が必要です。

  • 並列領域内でプライベートの変数、または parallel ディレクティブの reduction 句に出現する変数を、並列コンストラクトにバインドされる動作共有ディレクティブ上の lastprivate 句内で指定することはできません。

2.7.2.4 shared

この句は、variable-list 内に出現する変数を、チーム内のすべてのスレッド間で共有します。 チーム内のすべてのスレッドが、shared 変数に対する同じストレージ領域にアクセスします。

shared 句の構文は次のとおりです。

shared(variable-list)

2.7.2.5 default

default 句を使用すると、ユーザーが、変数のデータ共有属性に影響を与えることができます。 default 句の構文は次のとおりです。

default(shared | none)

default(shared) を指定することは、threadprivate または const で修飾されていない限り、shared 句内で現在表示されている各変数を明示的に示すことと同じです。 明示的な default 句がない場合、既定の動作は default(shared) が指定された場合と同じになります。

default(none) を指定すると、並列コンストラクトの字句エクステント内の変数に対するすべての参照について、少なくとも以下の 1 つを満たす必要があります。

  • 参照を含むコンストラクトのデータ共有属性句に、変数が明示的に示されている。

  • 並列コンストラクト内で変数が宣言されている。

  • 変数が threadprivate である。

  • 変数に const 修飾型がある。

  • 変数が for ディレクティブまたは parallel for ディレクティブの直後に続く for ループのループ コントロール変数で、変数の参照がループ内に出現する。

囲まれたディレクティブの firstprivate 句、lastprivate 句、または reduction 句で変数を指定すると、それを囲むコンテキスト内の変数への暗黙的な参照が発生します。 このような暗黙的の参照も、上記の要件に従います。

parallel ディレクティブ上で指定できる default 句は 1 つだけです。

次の例で示すように、変数の既定のデータ共有属性をオーバーライドするには、private 句、firstprivate 句、lastprivate 句、reduction 句、shared 句を使用します。

#pragma  omp  parallel  for  default(shared)  firstprivate(i)\
   private(x)  private(r)  lastprivate(i)

2.7.2.6 reduction

この句は、演算子 op を使用して、variable-list 内に出現するスカラー変数に対してリダクションを行います。 reduction 句の構文は次のとおりです。

reduction(op:variable-list)

リダクションは、通常、次のいずれかのフォームのステートメントに対して指定されます。

  • x=xopexpr
  • xbinop=expr
  • x=expropx (減算を除く)
  • x++
  • ++x
  • x--
  • --x

どこで:

x
リスト内で指定されたリダクション変数の 1 つ。

variable-list
スカラー リダクション変数のコンマ区切りのリスト。

expr
x を参照しないスカラー型の式。

op
オーバーロードされた演算子ではなく、+*-&^|&&、または || の 1 つ。

binop
オーバーロードされた演算子ではなく、+*-&^、または | の 1 つ。

次に reduction 句の例を示します。

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

この例に示すように、演算子は関数呼び出し内で非表示になっている場合があります。 ユーザーは、reduction 句内で指定された演算子がリダクション操作と一致することに注意する必要があります。

この例では || 演算子の右オペランドに副作用はありませんが、副作用は許可されています。ただし、これを使用するときは注意が必要です。 このコンテキストでは、ループの順次実行中には発生しないことが保証されている副作用が、並列実行中に発生する可能性があります。 この違いは、反復の実行順序が不確定であることが原因で発生する可能性があります。

この演算子は、コンパイラがリダクションに使用するプライベート変数の初期値と、最終処理演算子を決定するために使用されます。 演算子を明示的に指定することで、リダクション ステートメントを、コンストラクトの字句エクステント外に配置できます。 ディレクティブにはいくつでも reduction 句を指定できますが、そのディレクティブの最大 1 つの reduction 句に変数が出現する可能性があります。

variable-list 内の各変数のプライベート コピーが、private 句が使用されたかように、各スレッドに 1 つ作成されます。 プライベート コピーは、演算子に従って初期化されます (以下の表を参照)。

reduction 句が指定された領域の最後に、元のオブジェクトは、指定された演算子を使用して元の値と各プライベート コピーの最終的な値を組み合わせた結果が反映されるように更新されます。 リダクション演算子は、(減算を除き) すべて連想演算であり、コンパイラは最終値の計算を自由に再関連付けできます (減算リダクションの結果の一部は、最終値を形成するために加算されます)。

元のオブジェクトの値は、それを含む句に最初のスレッドが達すると不確定になり、リダクションの計算が完了するまでその状態が続きます。 通常、計算はコンストラクトの最後に完了しますが、nowait も適用されているコンストラクトで reduction 句が使用されている場合、バリア同期が実行され、すべてのスレッドが確実に reduction 句を完了するまで、元のオブジェクトの値は不確定なままです。

次の表は、有効な演算子とその正規の初期化値の一覧です。 実際の初期化値は、リダクション変数のデータ型と一致します。

Operator 初期化
+ 0
* 1
- 0
& ~0
| 0
^ 0
&& 1
|| 0

reduction 句に対する制限は次のとおりです。

  • reduction 句内の変数の型は、ポインター型と参照型が許可されないことを除き、リダクション演算子に対して有効である必要があります。

  • reduction 句内で指定される変数は、const で修飾することはできません。

  • 並列領域内でプライベートの変数、または parallel ディレクティブの reduction 句に出現する変数を、並列コンストラクトにバインドされる動作共有ディレクティブ上の reduction 句内で指定することはできません。

    #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 句は、並列領域を実行するチーム内のスレッドごとに、同じ値を threadprivate 変数に割り当てるメカニズムを提供します。 チームのマスター スレッド内の変数の値は、copyin 句内で指定された変数ごとに、割り当てられたかのように、並列領域の先頭にあるスレッド プライベート コピーにコピーされます。 copyin 句の構文は次のとおりです。

copyin(
variable-list
)

copyin 句に対する制限は次のとおりです。

  • copyin 句内で指定された変数には、アクセス可能で明確なコピー代入演算子が必要です。

  • copyin 句内で指定される変数は、threadprivate 変数である必要があります。

2.7.2.8 copyprivate

copyprivate 句は、プライベート変数を使用して、チームのメンバーからメンバーに値をブロードキャストするメカニズムを提供します。 このような共有変数を提供することが難しい場合 (レベルごとに異なる変数を必要とする再帰内など) に、値に共有変数を使用する代わりにこれを使用できます。 copyprivate 句は、single ディレクティブにのみ出現できます。

copyprivate 句の構文は次のとおりです。

copyprivate(
variable-list
)

variable-list 内の変数に対する copyprivate 句の影響は、single コンストラクトに関連付けられている構造化ブロックの実行後、チーム内のいずれかのスレッドが、コンストラクトの最後にバリアを離れる前に表れます。 その後、チーム内の他のすべてのスレッドで、variable-list 内の変数ごとに、その変数は、コンストラクトの構造化ブロックを実行したスレッド内の対応する変数の値を使用して、(割り当てられたかのように) 定義されるようになります。

copyprivate 句に対する制限は次のとおりです。

  • copyprivate 句で指定された変数は、同じ single ディレクティブの private 句または firstprivate 句内に出現することはできません。

  • 並列領域の動的エクステント内で copyprivate 句を持つ single ディレクティブが検出された場合、copyprivate 句で指定された変数はすべて、それを囲むコンテキストでプライベートである必要があります。

  • copyprivate 句内で指定された変数には、アクセス可能で明確なコピー代入演算子が必要です。

2.8 ディレクティブのバインディング

ディレクティブの動的バインドは、次の規則に従う必要があります。

  • forsectionssinglemasterbarrier ディレクティブは、そのディレクティブ上に存在する可能性のある if 句の値に関係なく、それを動的に囲む parallel (存在する場合) にバインドされます。 現在実行されている並列領域がない場合、ディレクティブは、マスター スレッドのみで構成されるチームによって実行されます。

  • ordered ディレクティブは、それを動的に囲む for にバインドされます。

  • atomic ディレクティブは、現在のチームだけでなく、すべてのスレッド内の atomic ディレクティブに対して排他アクセスを適用します。

  • critical ディレクティブは、現在のチームだけでなく、すべてのスレッド内の critical ディレクティブに対して排他アクセスを適用します。

  • ディレクティブは、それを動的に囲む最も近い parallel 外では、どのディレクティブにもバインドすることができません。

2.9 ディレクティブの入れ子

ディレクティブの動的な入れ子は、次の規則に従う必要があります。

  • parallel ディレクティブが別の parallel 内に動的に存在すると、入れ子の並列性が有効になってない限り、現在のスレッドのみで構成される新しいチームが論理的に確立されます。

  • 同じ parallel にバインドされる forsectionssingle ディレクティブは、相互に入れ子にできません。

  • 同じ名前の critical ディレクティブは、相互に入れ子にできません。 デッドロックを防ぐには、この制限では不十分であることに注意してください。

  • forsectionssingle ディレクティブが、領域と同じ parallel にバインドされている場合、それらのディレクティブは、criticalorderedmaster 領域の動的エクステント内で許可されません。

  • barrier ディレクティブが、領域と同じ parallel にバインドされている場合、そのディレクティブは、fororderedsectionssinglemastercritical 領域の動的エクステント内で許可されません。

  • master ディレクティブが、動作共有ディレクティブと同じ parallel にバインドされている場合、master ディレクティブは、forsectionssingle ディレクティブの動的エクステント内で許可されません。

  • ordered ディレクティブが、領域と同じ parallel にバインドされている場合、そのディレクティブは、critical 領域の動的エクステント内で許可されません。

  • 並列領域内で動的に実行されたときに許可されたディレクティブはすべて、並列領域外で実行されたときにも許可されます。 ユーザー指定の並列領域外で動的に実行された場合、ディレクティブは、マスター スレッドのみで構成されるチームによって実行されます。