Oracle® Solaris Studio 12.4: パフォーマンスアナライザ

印刷ビューの終了

更新: 2015 年 1 月
 
 

OpenMP実行の概要

OpenMP アプリケーションの実際の実行モデルについては、OpenMP の仕様 (たとえば、『OpenMP Application Program Interface, Version 3.0』の 1.3 セクションを参照) で説明されています。ただし、この仕様では、ユーザーにとって重要ないくつかの実装の詳細が規定されていません。Oracle の実際の実装は、直接記録されたプロファイリング情報を使用しても、スレッドがどのように相互作用するかをユーザーが容易に理解できないようになっています。

シングルスレッドプログラムが実行される場合と同様に、その呼び出しスタックが現在の位置と、どのようにしてそこまで到達したかを示すトレースを示します。このトレースは _start と呼ばれるルーチン内の開始命令から始まり、そのルーチンが main を呼び出し、それがさらに処理を進めてプログラム内のさまざまなサブルーチンを呼び出します。サブルーチンにループが含まれている場合、プログラムは、ループ終了条件が満たされるまでループ内のコードを繰り返し実行します。その後、実行は次のコードシーケンスへ進み、以後同様に処理が続きます。

プログラムが OpenMP で (自動並列化により) 並列化されている場合、動作は異なります。並列化されたプログラムの直感的なモデルでは、メインスレッド (マスタースレッド) が、シングルスレッドプログラムとまったく同じように実行されます。そのスレッドが並列ループまたは並列領域に到達すると、追加のスレーブスレッドが現れます。各スレッドはマスタースレッドのクローンであり、それらのすべてのスレッドが、異なる作業チャンクごとにループまたは並列領域の内容を並列に実行します。すべての作業チャンクが完了すると、すべてのスレッドの同期がとられ、スレーブスレッドが消失し、マスタースレッドが処理を続行します。

並列化されたプログラムの実際の動作は、それほど直接的なものではありません。コンパイラが並列領域またはループ用のコード (または、その他の任意の OpenMP 構造) を生成するとき、それらの内部のコードが抽出され、Oracle 実装で mfunction と呼ばれる独立した関数が作成されます。この関数は、アウトライン関数、またはループ本体関数とも呼ばれます。mfunction の名前は、OpenMP 構造タイプ、抽出元となった関数の名前、その構造が置かれているソース行の行番号を符号化したものです。これらの関数の名前は、パフォーマンスアナライザの上級モードとマシンモードで次の形式で表示されます。ここで、括弧内の名前はその関数の実際のシンボルテーブル名です。

bardo_ -- OMP parallel region from line 9 [_$p1C9.bardo_]
atomsum_ -- MP doall from line 7 [_$d1A7.atomsum_]

bardo_ -- 行 9 からの OMP 並列領域 [_$p1C9.bardo_] atomsum_ -- 行 7 からの MP doall [_$d1A7.atomsum_]これらの関数には、ほかのソース構造から生成される別の形式もあり、その場合、名前の中の OMP 並列領域は、MP コンストラクトMP doallOMP 領域のいずれかに置き換えられます。以降の説明では、これらすべてを総称して「並列領域」と言います。

並列ループ内のコードを実行する各スレッドは、mfunction を複数回呼び出すことができ、1 回呼び出すたびにループ内の 1 つの作業チャンクが実行されます。すべての作業チャンクが完了すると、それぞれのスレッドはライブラリ内の同期ルーチンまたは縮小ルーチンを呼び出します。その後、マスタースレッドが続行される一方、各スレーブスレッドはアイドル状態になり、マスタースレッドが次の並列領域に入るまで待機します。すべてのスケジューリングと同期は、OpenMP ランタイムの呼び出しによって処理されます。

並列領域内のコードは、その実行中、作業チャンクを実行しているか、ほかのスレッドとの同期をとっているか、行うべき追加の作業チャンクを取り出している場合があります。また、ほかの関数を呼び出す場合もあり、それによってさらに別の関数が呼び出される可能性もあります。並列領域内で実行されるスレーブスレッド (またはマスタースレッド) は、それ自体が、またはそれが呼び出す関数から、マスタースレッドとして動作し、独自の並列領域に入って入れ子並列を生成する場合があります。

パフォーマンスアナライザは、呼び出しスタックの統計的な標本収集に基づいてデータを収集し、すべてのスレッドにわたってデータを集計したあと、収集したデータの種類に基づいてパフォーマンスのメトリックを関数、呼び出し元と呼び出し先、ソース行、および命令に対して表示します。パフォーマンスアナライザは、OpenMP プログラムのパフォーマンスに関する情報を、ユーザーモード、上級モード、マシンモードの 3 つの表示モードのいずれかで表示します。

OpenMP プログラムのデータ収集についての詳細は、OpenMP のユーザーコミュニティー Web サイトの An OpenMP Runtime API for Profiling を参照してください。

OpenMP プロファイルデータのユーザー表示モード

プロファイルデータのユーザーモードの表示では、プログラムが実際にOpenMP実行の概要 で説明されている直観的モデルに従って実行されたかのように情報が提示されます。マシンモードで表示される実際のデータは、ランタイムライブラリ libmtsk.so の実装の詳細を取り込んだもので、これは、モデルに対応していません。上級モードでは、モデルに合うように変更されたデータと、実際のデータが表示されます。

ユーザーモードでは、プロファイルデータの表示はモデルにさらに近くなるよう変更されるため、記録されたデータやマシンモードの表示とは次の点で異なっています。

  • 擬似関数は、OpenMP ランタイムライブラリの視点から各スレッドの状態を表すように構築されます。

  • 呼び出しスタックは、前述のように、コードの実行方法のモデルに対応するデータを報告するよう操作されます。

  • クロックプロファイリング実験の場合、有効な作業の実行に費やされた時間と、OpenMP ランタイムでの待機に費やされた時間に対応する 2 つの追加のパフォーマンスメトリックが構築されます。メトリックは、OpenMP ワークメトリックとOpenMP 待ちメトリックです。

  • OpenMP 3.0 および 4.0 プログラムでは、3 番目のメトリックである「OpenMP オーバーヘッド」が構築されます。

擬似関数

擬似関数は、スレッドが OpenMP ランタイムライブラリ内でいずれかの状態にあったイベントを反映するために構築され、ユーザーモードおよび上級モード呼び出しスタック上に置かれます。

次の疑似関数が定義されています。

<OMP-overhead>
OpenMP ライブラリ内で実行中
<OMP-idle>
作業を待っているスレーブスレッド
<OMP-reduction>
縮約操作を実行中のスレッド
<OMP-implicit_barrier>
暗黙のバリアで待機中のスレッド
<OMP-explicit_barrier>
明示的なバリアで待機中のスレッド
<OMP-lock_wait>
ロックを待っているスレッド
<OMP-critical_section_wait>
critical セクションに入るのを待っているスレッド
<OMP-ordered_section_wait>
ordered セクションに入る順番を待っているスレッド
<OMP-atomic_wait>
OpenMP 原子構造で待機中のスレッド

スレッドが、擬似関数のいずれかに対応する OpenMP ランタイム状態にある場合、擬似関数がスタック上のリーフ関数として追加されます。スレッドの実際のリーフ関数が OpenMP ランタイム内のどこかに存在する場合、その関数は、リーフ関数として <OMP-overhead> に置き換えられます。そうでない場合、OpenMP ランタイムに入っているすべての PC は、ユーザーモードスタックから除外されます。

OpenMP 3.0 および 4.0 プログラムでは、<OMP-overhead> 擬似関数は使用されません。この擬似関数は、OpenMP オーバーヘッドメトリックに置き換えられています。

ユーザーモード呼び出しスタック

OpenMP 実験の場合は、OpenMP を使用せずにプログラムがコンパイルされたときに取得されたものと同様の、再構築された呼び出しスタックが表示されます。目標は、実際の処理の詳細すべてを示すことではなく、プログラムを直観的に理解できるようにプロファイルデータを表示することです。マスタースレッドとスレーブスレッドの呼び出しスタックが調整され、OpenMP ランタイムライブラリが特定の操作を実行しているときは擬似関数 <OMP-*> が呼び出しスタックに追加されます。

OpenMP メトリック

OpenMP プログラムの時間プロファイルイベントを処理するときは、OpenMP システム内の 2 つの状態でそれぞれ費やされた時間に対応する 2 つのメトリックが示されます。「OpenMP ワーク」と「OpenMP 待ち」です。

スレッドがユーザーコードから実行されたときは、逐次か並列かを問わず、「OpenMP ワーク」に時間が累積されます。スレッドが何かを待って先に進めないときは、その待機が busy-wait (spin-wait) であるかスリープ状態であるかを問わず、「OpenMP 待ち」に時間が累積されます。これら 2 つのメトリックの合計は、時間プロファイル内の総スレッド数メトリックに一致します。

OpenMP 待ちメトリックと OpenMP ワークメトリックは、ユーザーモード、上級モード、およびマシンモードに表示されます。

OpenMP プロファイリングデータの上級表示モード

上級表示モードで OpenMP 実験を確認する場合、OpenMP ランタイムが特定の操作を実行しているときは、ユーザー表示モードと同様に <OMP-*> という形式の擬似関数が表示されます。ただし、上級表示モードでは、並列化されたループ、タスクなどを表す、コンパイラにより生成された mfunction が別個に表示されます。ユーザーモードでは、コンパイラにより生成されたこれらの mfunction はユーザー関数に含められます。

OpenMP プロファイリングデータのマシン表示モード

「マシン」モードでは、すべてのスレッドのネイティブな呼び出しスタックと、コンパイラによって生成されたアウトライン関数が表示されます。

実行のさまざまなフェーズでのプログラムの実際の呼び出しスタックは、ユーザーモデルで説明したものとはまったく異なります。マシンモードでは、呼び出しスタックが測定どおりに表示され、変換は行われず、擬似関数も構築されません。ただし、時間プロファイルのメトリックは依然として示されます。

次に示す各呼び出しスタックでは、libmtsk は OpenMP ランタイムライブラリ内の呼び出しスタックに入っている 1 つ以上のフレームを表しています。どの関数がどの順序で表示されるかの詳細は、バリア用のコードまたは縮小を行うコードの内部的な実装と同様に、OpenMP のリリースによって異なります。

  1. 最初の並列領域の前

    最初の並列領域の前最初の並列領域に入る前の時点で存在するスレッドは、マスタースレッドただ 1 つだけです。呼び出しスタックはユーザーモードおよび上級モードの場合と同じです。

    マスター
    foo
    main
    _start
  2. 並列領域内で実行中

    マスター
    スレーブ 1
    スレーブ 2
    スレーブ 3
    foo-OMP...
    libmtsk
    foo
    foo-OMP...
    foo-OMP...
    foo-OMP...
    main
    libmtsk
    libmtsk
    libmtsk
    _start
    _lwp_start
    _lwp_start
    _lwp_start

    マシンモードでは、スレーブスレッドはマスターが開始された _start 内ではなく、_lwp_start 内で開始されたものとして示されます。一部のバージョンのスレッドライブラリでは、この関数は _thread_start として表示されます。foo-OMP... の呼び出しは、並列領域に対して生成された mfunction を表します。

  3. すべてのスレッドがバリアの位置にある

    マスター
    スレーブ 1
    スレーブ 2
    スレーブ 3
    libmtsk
    foo-OMP...
    foo
    libmtsk
    libmtsk
    libmtsk
    main
    foo-OMP...
    foo-OMP...
    foo-OMP...
    _start
    _lwp_start
    _lwp_start
    _lwp_start

    スレッドが並列領域内で実行されるときと異なり、スレッドがバリアの位置で待機しているときは、foo と並列領域コード foo-OMP...... の間に OpenMP ランタイムからのフレームは存在しません。その理由は、実際の実行には OMP 並列領域関数が含まれていませんが、OpenMP ランタイムがレジスタを操作し、スタック展開で直前に実行された並列領域関数からランタイムバリアコードへの呼び出しが示されるようにするからです。そうしないと、どの並列領域がバリア呼び出しに関連しているかをマシンモードで判定する方法がなくなってしまいます。

  4. 並列領域から出たあと

    マスター
    スレーブ 1
    スレーブ 2
    スレーブ 3
    foo
    main
    libmtsk
    libmtsk
    libmtsk
    _start
    _lwp_start
    _lwp_start
    _lwp_start

    スレーブスレッド内では、呼び出しスタック上にユーザーフレームが存在しません。

  5. 入れ子の並列領域内にいるとき

    マスター
    スレーブ 1
    スレーブ 2
    スレーブ 3
    スレーブ 4
    bar-OMP...
    foo-OMP...
    libmtsk
    libmtsk
    bar
    foo
    foo-OMP...
    foo-OMP...
    foo-OMP...
    bar-OMP...
    main
    libmtsk
    libmtsk
    libmtsk
    libmtsk
    _start
    _lwp_start
    _lwp_start
    _lwp_start
    _lwp_start