並列処理モデル

OpenMP* ディレクティブを含むプログラムは、単一のスレッド (プライマリー・スレッド) として実行を開始します。最初の並列構造が検出されるまで、プライマリー・スレッドは、シーケンシャルに実行します。

PARALLEL と END PARALLEL ディレクティブは並列構造の範囲を定義します。プライマリー・スレッドが並列構造を検出すると、そのプライマリー・スレッドがチームのプライマリー・スレッドとなり、スレッドのチームを作成します。並列構造に囲まれたプログラム文はすべて (そこから呼び出されるすべてのルーチンを含む)、チーム内の各スレッドごとに並列で実行されます。

構造内で字句的に囲まれた文は、構造の静的範囲を定義します。動的範囲には、スレッドによる構造の実行中に実行されるすべての文 (呼び出される全ルーチンを含む) が含まれます。

1 つのスレッドが並列構造によって囲まれた構造化ブロックの終わりに到達すると、そのスレッドは、チーム内のすべてのスレッドが到着するまで待機します。すべてのスレッドが到着すると、チームは消滅し、プライマリー・スレッドのみが次の並列構造のコードの実行を続けます。チーム内の他のスレッドは、別のチームの形成で必要になるまで待機状態になります。単一プログラム内で並列構造は何回でも指定できます。結果として、プログラム実行中に、スレッドのチームは何度も生成され消滅します。

次の例は、上位レベルから OpenMP* 構造の実行モデルを示します。コードのコメントは、各構造やセクションの説明です。

PROGRAM MAIN           ! シリアル実行の開始
  ...                  ! プライマリー・スレッドのみ実行
 !$OMP PARALLEL        ! 並列構造を開始してチームを形成
   ...                 ! このコードは各チームメンバーにより実行される
  !$OMP SECTIONS       ! ワークシェア構造の開始
    !$OMP SECTION      ! 1 つの作業単位
     ...               ! 
    !$OMP SECTION      ! 別の作業単位
     ...               ! 
   !$OMP END SECTIONS  ! 両方の作業単位の完了を待機
   ...                 ! 複製されたコード
  !$OMP DO             ! ワークシェア構造を開始
     DO                ! 各反復が作業単位
     ...               ! チーム内で作業を分配
     END DO            ! 
  !$OMP END DO NOWAIT  ! ワークシェア構造の終了
                       ! NOWAIT が指定されているので待機不要
                       ! このコードは各チームメンバーにより実行される
  !$OMP CRITICAL       ! クリティカル構造の開始
     ...               ! 一度に 1 つのスレッドでのみ実行
  !$OMP END CRITICAL   ! クリティカル構造の終了
   ...                 ! このコードは各チームメンバーにより実行される
  !$OMP BARRIER        ! すべてのチームメンバーが到着するまで待機
   ...                 ! このコードは各チームメンバーにより実行される
 !$OMP END PARALLEL    ! 並列構造の終了
                       ! チームを解散してシリアル実行を継続
  ...                  ! 別の並列構造 
END PROGRAM MAIN       ! シリアル実行の終了

親なしディレクティブの使用

並列構造内で呼び出されたルーチンで、ディレクティブを使用することができます。並列構造の静的範囲ではなく、動的範囲のディレクティブは、親なしディレクティブと呼ばれます。親なしディレクティブは、プログラムのシーケンシャル・バージョンに最小限の変更を行うだけで、プログラム部分を並列に実行できます。この機能を使用すると、プログラム・コール・ツリーの最上位レベルで並列構造をコーディングでき、ディレクティブを使用して呼び出されるすべてのルーチンの実行を制御することができます。次に例を示します。

subroutine F 
... 
!$OMP PARALLEL...
   call G 
... 
subroutine G 
!$OMP DO...! 親なしディレクティブ
...

並列領域が サブルーチン G で字句的に指定されていないので、これが親なし DO ディレクティブとなります。

データ環境

構造でサポートされるデータ環境節を使用して、OpenMP* 構造のデータ環境を制御できます。また、THREADPRIVATE ディレクティブを使用して、名前付きグローバルライフタイム・オブジェクトをプライベートにできます。

データ環境節の一覧は、OpenMP* 仕様を参照してください。以下は、一般的に使用されているものです。

複数のディレクティブを使用して、変数のデータスコープ属性を指定した構造の継続期間中にその属性を制御することができます。ただし、ディレクティブでデータスコープ属性節を指定しない場合、ディレクティブに影響を受ける変数の動作はデフォルトのスコープ規則によって決まります。これは、OpenMP* 仕様で説明されています。

使用するスレッド数の決定

ワークロードがアプリケーションの (一定ではない) 入力に依存するアプリケーションでは、入力サイズが確認されるランタイム時まで、使用するスレッド数の決定を保留します。スレッド数に影響を与えるワークロードの入力引数には、行列のサイズ、データベースのサイズ、イメージ/ビデオのサイズおよび解像度、ツリーベースの構造体の階層の深さ/幅/種類、およびリストベースの構造体のサイズなどが含まれます。同様に、プロセッサー数が一定ではないシステムで実行するように設計されたアプリケーションでも、マシンサイズが確認されるアプリケーション・ランタイム時まで、使用するスレッド数の選択を保留します。

入力データから処理の量を予測できないアプリケーションでは、適切なスレッド数を選択するために、測定ステップを取り入れて、ワークロードおよびシステムの特性を確認することを推奨します。測定結果は、ファイルシステムなど永久保管場所に格納することによって、(測定ステップのオーバーヘッドを抑え) 永続的に使用することができます。

システムの処理ユニット数を超えるスレッドを同時に使用しないでください。同時に使用すると、オペレーティング・システムによりプロセッサー上でスレッドが多重化され、パフォーマンスが最適化されません。

ライブラリーを開発する場合 (アプリケーション全体を開発する場合とは異なり)、ライブラリーによって使用されるスレッド数を簡単に選択できるメカニズムを使用してください。これは、ユーザーが外側のレベルの並列化を行った場合、ライブラリー内の並列化は必要ないためです。

並列領域で NUM_THREADS 節を使用して、使用するスレッド数を制御し、並列領域で IF 節を使用してマルチスレッドを使用するかどうかを決定します。OMP_SET_NUM_THREADS() ルーチンも使用することができますが、呼び出し側のスレッドにより作成される並列領域にも影響します。NUM_THREADS 節の効果はローカルであるため、ほかの並列領域には影響しません。明示的なスレッド数の設定には、次のような短所があります。

  1. 多数のプロセッサーを搭載したシステムで、アプリケーションはすべてのプロセッサーではなく、その一部を使用します。

  2. 少数のプロセッサーを搭載したシステムで、アプリケーションはパフォーマンスの低下につながるオーバーサブスクリプションを強制する可能性があります。

インテルの OpenMP* ランタイムは、OMP_SET_NUM_THREADS() ルーチンを使用しない限り、利用可能な論理プロセッサー数と同じ数のスレッドを生成します。実際の制限を特定するには、OMP_GET_THREAD_LIMIT() と OMP_GET_MAX_ACTIVE_LEVELS() を使用します。システムがオーバーロードしないように、開発者はスレッドの使用と並列処理の入れ子構造に注意する必要があります。OMP_THREAD_LIMIT 環境変数は、OpenMP* プログラム全体に使用する OpenMP* スレッドの数を制限します。OMP_MAX_ACTIVE_LEVELS 環境変数は、アクティブな入れ子構造の並列領域の数を制限します。

セットと領域のバインド

OpenMP* 構文のバインドタスクは、領域の実行により影響を受ける、または領域の実行にコンテキストを与えるタスクのセットです。これは、すべてのタスク、現在のチームタスク、領域で生成された現在のチームのすべてのタスク、暗黙のバインドタスク、または生成されるタスクになります。

OpenMP* 構文のバインドスレッドは、領域の実行により影響を受ける、または領域の実行にコンテキストを与えるスレッドのセットです。これは、デバイス上のすべてのスレッド、競合グループのすべてのスレッド、チーム領域を実行するすべてのプライマリー・スレッド、現在のチーム、または到達スレッドになります。

OpenMP* 構文のバインド領域は、実行コンテキストとディレクティブの有効範囲を決定する領域です。