OpenMP* API コンパイラー宣言子を持つプログラムは、単一のスレッドとして実行を開始します。これは、初期スレッドの実行と呼ばれます。最初の並列構造が検出されるまで、初期スレッドは、シーケンシャルに実行します。
OpenMP API では、#pragma omp parallel 宣言子は並列構造の範囲を定義します。初期スレッドが並列構造を検出すると、その初期スレッドがチームのマスターとなり、スレッドのチームを作成します。並列構造に囲まれたプログラム文はすべて (そこから呼び出されるすべてのルーチンを含む)、チーム内の各スレッドごとに並列で実行されます。
構造内で字句的に囲まれた文は、構造の静的範囲を定義します。動的範囲には、スレッドによる構造の実行中に実行されるすべての文 (呼び出される全ルーチンを含む) が含まれます。
1 つのスレッドが並列構造によって囲まれた構造ブロックの終わりに到達すると、そのスレッドは、チーム内のすべてのスレッドが到着するまで待機します。すべてのスレッドが到着すると、チームは消滅し、マスタースレッドのみが次の並列構造のコードの実行を続けます。チーム内の他のスレッドは、別のチームの形成で必要になるまで待機状態になります。単一プログラム内で並列構造は何回でも指定できます。結果として、プログラム実行中に、スレッドのチームは何度も生成され消滅します。
次の例は、上位レベルから OpenMP 構造の実行モデルを示します。コードのコメントは、各構造やセクションの説明です。
例 |
---|
main() { // Begin serial execution. ... // Only the initial thread executes #pragma omp parallel // Begin a parallel construct and form { // a team. #pragma omp sections // Begin a worksharing construct. { #pragma omp section // One unit of work. {...} #pragma omp section // Another unit of work. {...} } // Wait until both units of work complete. ... // This code is executed by each team member. #pragma omp for nowait // Begin a worksharing Construct for(...) { // Each iteration chunk is unit of work. ... // Work is distributed among the team members. } // End of worksharing construct. // nowait was specified so threads proceed. #pragma omp critical // Begin a critical section. {...} // Only one thread executes at a time. ... // This code is executed by each team member. #pragma omp barrier // Wait for all team members to arrive. ... // This code is executed by each team member. } // End of Parallel Construct // Disband team and continue serial execution. ... // Possibly more parallel constructs. } // End serial execution. |
並列構造内で呼び出されたルーチンで、宣言子を使用することができます。並列構造の静的範囲ではなく、動的範囲の宣言子は、孤立宣言子と呼ばれます。孤立宣言子は、プログラムのシーケンシャル・バージョンに最小限の変更を行うだけで、プログラム部分を並列に実行できます。この機能を使用すると、プログラム・コール・ツリーの最上位レベルで並列構造をコーディングでき、宣言子を使用して呼び出されるすべてのルーチンの実行を制御することができます。次に例を示します。
例 |
---|
int main(void) { #pragma omp parallel { phase1(); } } void phase1(void) { #pragma omp for // This is an orphaned pragma. for(i=0; i < n; i++) { some_work(i); } } |
並列領域が routine phase 1 で字句的に指定されていないので、これが孤立 for ループ宣言子となります。
データ環境制御
並列およびワークシェアリング構造内でデータ環境を制御できます。宣言子と宣言子のデータ環境節を使用して、THREADPRIVATE 宣言子で名前付きグローバル・ライフタイム・オブジェクトをプライベート化したり、またはデータ環境節をサポートする宣言子のデータ環境節でデータスコープ属性を制御できます。
データスコープ属性節:
default
private
firstprivate
lastprivate
reduction
shared
データコピー節:
copyin
copyprivate
複数の宣言子節を使用して、変数のデータスコープ属性を指定した構造の継続期間中にその属性を制御することができます。ただし、宣言子でデータスコープ属性節を指定しない場合、宣言子に影響を受ける変数の動作はデフォルトのスコーピング規則によって決まります。これは、OpenMP API 仕様で説明されています。
ワークロードがアプリケーションの (一定ではない) 入力に依存するアプリケーションでは、入力サイズが確認されるランタイム時まで、使用するスレッド数の決定を保留します。スレッド数に影響を与えるワークロードの入力パラメーターには、行列のサイズ、データベースのサイズ、イメージ/ビデオのサイズおよび解像度、ツリーベースの構造体の階層の深さ/幅/種類、およびリストベースの構造体のサイズなどが含まれます。同様に、プロセッサー数が一定ではないシステムで実行するように設計されたアプリケーションでも、マシンサイズが確認されるアプリケーション・ランタイム時まで、使用するスレッド数の決定を保留します。
入力データから処理の量を予測できないアプリケーションでは、適切なスレッド数を選択するために、測定ステップを取り入れて、ワークロードおよびシステムの特性を確認することを推奨します。測定結果は、ファイルシステムなど永久保管場所に格納することによって、(測定ステップのオーバーヘッドを抑え) 永続的に使用することができます。
システムのプロセシング・ユニット数を超えるスレッドを同時に使用しないでください。同時に使用すると、オペレーティング・システムによるプロセッサーへの多重送信が発生し、パフォーマンスが最適化されません。
ライブラリーを開発する場合 (アプリケーション全体を開発する場合とは異なり)、ライブラリーによって使用されるスレッド数を簡単に選択できるメカニズムを使用してください。これは、より高度な並列化を使用している場合、ライブラリー内の並列化が必要ないためです。
並列領域で num_threads 節を使用して、使用するスレッド数を制御し、並列領域で if 節を使用してマルチスレッドを使用するかどうかを決定します。omp_set_num_threads ルーチンも使用することができますが、呼び出し側のスレッドにより作成される並列領域にも影響します。num_threads 節の効果はローカルであるため、ほかの並列領域には影響しません。
デフォルトでは、インテルの OpenMP ランタイムは多くのスレッドとアクティブな入れ子された並列領域を作成することができます。OMP_GET_THREAD_LIMIT() および OMP_GET_MAX_ACTIVE_LEVELS() を使用して、それぞれの数を制限することができます。システムがオーバーロードしないように、開発者はスレッドの使用と並列処理の入れ子に注意する必要があります。OMP_THREADS_LIMIT 環境変数は、OpenMP プログラム全体に使用する OpenMP スレッドの数を制限します。OMP_MAX_ACTIVE_LEVELS 環境変数は、アクティブな入れ子された並列領域の数を制限します。