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 の場合、領域はシリアル化されます。
要求されたスレッド数を確認するために、次の規則が順番に考慮されます。 条件が満たされた最初の規則が適用されます。
num_threads
句が存在する場合、整数式の値が、要求されたスレッドの数である。omp_set_num_threads
ライブラリ関数が呼び出された場合、直前に実行された呼び出しの引数の値が、要求されたスレッドの数である。環境変数
OMP_NUM_THREADS
が定義されている場合、この環境変数の値が、要求されたスレッド数である。上記のいずれの方法も使用しない場合、要求されたスレッドの数が、実装で定義される。
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
句の評価の順序は指定されていません。
クロスリファレンス
private
句、firstprivate
句、default
句、shared
句、copyin
句、reduction
句 (セクション 2.7.2)- OMP_NUM_THREADS 環境変数
- omp_set_dynamic ライブラリ関数
- OMP_DYNAMIC 環境変数
- omp_set_nested 関数
- OMP_NESTED 環境変数
- omp_set_num_threads ライブラリ関数
2.4 動作共有のコンストラクト
動作共有のコンストラクトは、関連するステートメントの実行を、それを検出したチームのメンバーに分散します。 動作共有のディレクティブが新しいスレッドを起動することはありません。また、動作共有のコンストラクトのエントリ上に暗黙のバリアはありません。
検出された動作共有のコンストラクトと barrier
ディレクティブのシーケンスは、チーム内のすべてのスレッドで同じである必要があります。
OpenMP では、次の動作共有のコンストラクトが定義されています。これらのコンストラクトについては、後のセクションで説明します。
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
次のいずれか:
<
<=
>
>=
lb、b、incr
ループ インバリアント整数式。 これらの式の評価中は同期が行われません。このため、評価された副作用によって不確定な結果がもたらされます。
正規の形式を使用すると、ループに入るときにループ反復の数を計算できます。 この計算は、整数の上位変換後、var 型の値を使用して行われます。 特に、b -
lb +
incr の値をその型で表すことができない場合、結果は不確定になります。 さらに、logical-op が <
または <=
である場合、incr-expr は、ループの反復ごとに var を増やす必要があります。 logical-op が >
または >=
である場合、incr-expr は、ループの反復ごとに var を小さくしなければなりません。
schedule
句は、for
ループの反復が、チームのスレッド間にどのように分割されるかを指定します。 プログラムの正確性は、どのスレッドが特定の反復を実行するかに依存してはいけません。 chunk_size の値は (指定されている場合)、正の値を持つループ インバリアント整数式である必要があります。 この式の評価中は同期が行われません。このため、評価された副作用によって不確定な結果がもたらされます。 スケジュール kind には、以下のいずれかの値を指定できます。
表 2-1: schedule
句 kind の値
Value | 説明 |
---|---|
static | schedule(static, chunk_size ) が指定されている場合、反復は chunk_size で指定されたサイズのチャンクに分割されます。 チャンクは、チーム内のスレッドに、ラウンド ロビン方式でスレッド番号順に静的に割り当てられます。 chunk_size が指定されていない場合、反復空間はほぼ同じサイズのチャンクに分割され、各スレッドに 1 つのチャンクが割り当てられます。 |
動的 | 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_size、lb、b、incr 式の中で副作用が発生するかどうか、また、どのくらいの頻度で発生するかは特定できません。
chunk_size 式の値は、チーム内のすべてのスレッドで同じである必要があります。
クロスリファレンス
private
句、firstprivate
句、lastprivate
句、reduction
句 (セクション 2.7.2)- OMP_SCHEDULE 環境変数
- ordered コンストラクト
- schedule 句
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 つだけです。
クロスリファレンス
private
句、firstprivate
句、lastprivate
句、reduction
句 (セクション 2.7.2)
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
句と共に使用することはできません。
クロスリファレンス
private
句、firstprivate
句、copyprivate
句 (セクション 2.7.2)
2.5 結合された並列動作共有のコンストラクト
結合された並列動作共有のコンストラクトは、動作共有のコンストラクトが 1 つしかない並列領域を指定するためのショートカットです。 これらのディレクティブのセマンティクスは、parallel
ディレクティブの後に 1 つの動作共有コンストラクトを明示的に指定することと同じです。
以下のセクションでは、結合された並列動作共有のコンストラクトについて説明します。
- parallel for ディレクティブ
- parallel sections ディレクティブ
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
ディレクティブは、自動ストレージ期間が指定されているアクセスできないオブジェクト以外の、すべての共有オブジェクトを同期します (variable-list を含む flush
よりも、オーバーヘッドが大きくなる可能性があります)。variable-list のない 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
変数の値が、その変数の元のオブジェクトに割り当てられます。 for
や parallel 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
=
x op expr - x binop
=
expr - x
=
expr op x (減算を除く) - 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 ディレクティブのバインディング
ディレクティブの動的バインドは、次の規則に従う必要があります。
for
、sections
、single
、master
、barrier
ディレクティブは、そのディレクティブ上に存在する可能性のあるif
句の値に関係なく、それを動的に囲むparallel
(存在する場合) にバインドされます。 現在実行されている並列領域がない場合、ディレクティブは、マスター スレッドのみで構成されるチームによって実行されます。ordered
ディレクティブは、それを動的に囲むfor
にバインドされます。atomic
ディレクティブは、現在のチームだけでなく、すべてのスレッド内のatomic
ディレクティブに対して排他アクセスを適用します。critical
ディレクティブは、現在のチームだけでなく、すべてのスレッド内のcritical
ディレクティブに対して排他アクセスを適用します。ディレクティブは、それを動的に囲む最も近い
parallel
外では、どのディレクティブにもバインドすることができません。
2.9 ディレクティブの入れ子
ディレクティブの動的な入れ子は、次の規則に従う必要があります。
parallel
ディレクティブが別のparallel
内に動的に存在すると、入れ子の並列性が有効になってない限り、現在のスレッドのみで構成される新しいチームが論理的に確立されます。同じ
parallel
にバインドされるfor
、sections
、single
ディレクティブは、相互に入れ子にできません。同じ名前の
critical
ディレクティブは、相互に入れ子にできません。 デッドロックを防ぐには、この制限では不十分であることに注意してください。for
、sections
、single
ディレクティブが、領域と同じparallel
にバインドされている場合、それらのディレクティブは、critical
、ordered
、master
領域の動的エクステント内で許可されません。barrier
ディレクティブが、領域と同じparallel
にバインドされている場合、そのディレクティブは、for
、ordered
、sections
、single
、master
、critical
領域の動的エクステント内で許可されません。master
ディレクティブが、動作共有ディレクティブと同じparallel
にバインドされている場合、master
ディレクティブは、for
、sections
、single
ディレクティブの動的エクステント内で許可されません。ordered
ディレクティブが、領域と同じparallel
にバインドされている場合、そのディレクティブは、critical
領域の動的エクステント内で許可されません。並列領域内で動的に実行されたときに許可されたディレクティブはすべて、並列領域外で実行されたときにも許可されます。 ユーザー指定の並列領域外で動的に実行された場合、ディレクティブは、マスター スレッドのみで構成されるチームによって実行されます。