Sun Studio 12: パフォーマンスアナライザ

パフォーマンスメトリックの解釈

各イベントのデータには、高分解能のタイムスタンプ、スレッド ID、LWP ID、プロセッサ ID が含まれます。これらの最初の 3 つのデータを使用すれば、時間、スレッド、または LWP によってパフォーマンスアナライザでメトリックのフィルタ処理が行えます。プロセッサ ID については、getcpuid(2) のマニュアルページを参照してください。getcpuid を利用できないシステムでのプロセッサ ID は -1 であり、Unknown にマップされます。

各イベントでは、共通データ以外に、以降の節で説明する固有の raw データが生成されます。これらの節ではまた、raw データから得られるメトリックの精度と、データ収集がメトリックに及ぼす影響についても説明しています。

時間ベースのプロファイリング

時間ベースのプロファイリングのイベント固有のデータは、プロファイル間隔カウント値からなる配列で構成されています。Solaris OS の場合は、間隔カウンタが提供されます。プロファイル間隔の最後で適切な間隔カウンタが 1 増分され、別のプロファイル信号がスケジューリングされます。この配列が記録され、リセットされるのは、Solaris LWP スレッドが CPU ユーザーモードに入った場合だけです。配列のリセット時には、ユーザー CPU 状態の配列要素が 1 に設定され、ほかの全状態の配列要素が 0 に設定されます。配列データが記録されるのは、配列がリセットされる前にユーザーモードに入るときです。したがって、配列には、Solaris LWP ごとにカーネルが保持する 10 個のマイクロステートのそれぞれについて、ユーザーモードに前回入って以降の各マイクロステートのカウントの累計値が含まれます。Linux OS ではマイクロステートは存在せず、利用できる間隔カウンタはユーザー CPU 時間だけです。

呼び出しスタックは、データと同時に記録されます。プロファイル間隔の最後で Solaris LWP がユーザーモードでない場合は、LWP またはスレッドが再びユーザーモードになるまで、呼び出しスタックの内容は変わりません。すなわち、呼び出しスタックには、各プロファイル間隔の最後のプログラムカウンタの位置が常に正確に記録されます。

表 7–2 に、各マイクロステートとメトリックの、Solaris OS における対応関係をまとめます。

表 7–2 カーネルのマイクロステートとメトリックの対応関係

カーネルのマイクロステート 

内容の説明 

メトリック名 

LMS_USER

ユーザーモードで動作 

ユーザー CPU 時間 

LMS_SYSTEM

システムコールまたはページフォルトで動作 

システム CPU 時間 

LMS_TRAP

上記以外のトラップで動作 

システム CPU 時間 

LMS_TFAULT

ユーザーテキストページフォルトでスリープ 

テキストページフォルト時間 

LMS_DFAULT

ユーザーデータページフォルトでスリープ 

データページフォルト時間 

LMS_KFAULT

カーネルページフォルトでスリープ 

ほかの待ち時間 

LMS_USER_LOCK

ユーザーモードロック待ちのスリープ 

ユーザーロック時間 

LMS_SLEEP

ほかの理由によるスリープ 

ほかの待ち時間 

LMS_STOPPED

停止 (/proc、ジョブ制御、lwp_stop のいずれか)

ほかの待ち時間 

LMS_WAIT_CPU

CPU 待ち 

CPU 待ち時間 

タイミングメトリックの精度

タイミングデータは統計データとして収集されます。 このため、ほかの統計的な標本収集手法と同様に、あらゆる誤差の影響を受けます。プログラムの実行時間が非常に短い場合は、少数のプロファイルパケットしか記録されず、多くのリソースを消費するプログラム部分が、呼び出しスタックに反映されないことがあります。このため、目的の関数またはソース行について数百のプロファイルパケットを蓄積するのに十分な時間または十分な回数に渡って、プログラムを実行するようにしてください。

統計的な標本収集の誤差のほかに、データの収集・関連付け方法、システムにおけるプログラムの実行の進み具合を原因とする誤差もあります。次に示す環境などでは、タイミングメトリックでデータに不正確さやひずみが生じる可能性があります。

これらの不正確さのほかにも、データ収集処理そのものが原因でタイミングメトリックが不正確になります。記録はプロファイルシグナルによって開始されるため、プロファイルパケットの記録に費やされた時間が、プログラムのメトリックに反映されることはありません。これは、相関関係の別の例です。記録に費やされたユーザー CPU 時間は、記録されるあらゆるマイクロステート値に配分されます。この結果、ユーザー CPU 時間のメトリックが実際より小さくなり、その他のメトリックが実際より大きくなります。デフォルトのプロファイル間隔の場合、一般に、データの記録に費やされる時間は CPU 時間の 2、3% 未満です。

タイミングメトリックの比較

時間ベースの実験のプロファイリングで得られたタイミングメトリックと、その他の方法で得られた時間を比較する場合は、次の点に注意する必要があります。

シングルスレッドアプリケーションの場合、通常 1 つのプロセスについて記録された Solaris LWP または Linux スレッド合計時間は、同じプロセスについて gethrtime(3C) によって返される値と比較すると、数十分の 1 パーセントの精度になります。CPU 時間の場合は、同じプロセスについて gethrvtime(3C) によって返される値と比較して、数パーセント程度異なることがあります。負荷が大きい場合は、差がさらに大きくなることがあります。ただし、CPU 時間の差は規則的なひずみを表すものではなく、関数、ソース行などについて報告される相対時間に大きなひずみはありません。

Solaris OS の非結合スレッドを使用するマルチスレッドアプリケーションの場合、gethrvtime() によって返される値の差が無意味であることがあります。これは、gethrvtime() は LWP について値を返し、スレッドは LWP ごとに異なることがあるからです。

パフォーマンスアナライザの報告する LWP 時間が、vmstat の報告する時間とかなり異なることがあります。 これは、vmstat が CPU 全体にまたがって集計した時間を報告するためです。たとえば、ターゲットプロセスの LWP 数が、そのプロセスが動作するシステムの CPU 数よりも多い場合、アナライザは、vmstat が報告する時間よりもずっと長い待ち時間を報告します。

パフォーマンスアナライザの「統計」タブと er_print 統計ディスプレイに表示されるマイクロステート時間値は、プロセスファイルシステムの /proc 使用報告に基づいており、この報告には、マイクロステートで費やされる時間が高い精度で記録されます。詳細は、proc (4) のマニュアルページを参照してください。これらのタイミング値と <合計> 関数 (プログラム全体を表す) のメトリックを比較することによって、集計されたタイミングメトリックのおおよその精度を知ることができます。ただし、「統計」タブに表示される値には、<合計> の時間メトリック値に含まれないその他の寄与要素が含まれることがあります。その原因は、データ収集が一時停止される期間によるものです。

ユーザー CPU 時間とハードウェアカウンタサイクル時間は異なります。なぜなら、ハードウェアカウンタは、CPU モードがシステムモードへ切り替えられたときにオフにされるからです。詳細は、「トラップ」を参照してください。

同期待ちトレースデータ

同期待ちトレースは、Solaris プラットフォームでのみ利用できます。コレクタは、スレッドライブラリ libthread.so 内の関数の呼び出しまたは リアルタイム拡張ライブラリ librt.so の呼び出しをトレースすることによって、同期遅延イベントのデータを収集します。イベント固有のデータは、要求と許可 (トレース対象の呼び出しの始まりと終わり) の高分解能のタイムスタンプと同期オブジェクト (要求された相互排他ロックなど) のアドレスで構成されます。スレッド ID と LWP ID は、データが記録された時点での ID です。 要求時刻と許可時刻の差が待ち時間です。記録されるイベントは、指定したしきい値を要求と許可の時間差が超えたものだけです。同期待ちトレースデータは、許可時に実験ファイルに記録されます。

遅延の原因となったイベントが完了しないかぎり、待ちスレッドがスケジューリングされている LWP がほかの作業を行うことはできません。この待ち時間は、「同期待ち時間」と「ユーザーロック時間」の両方に反映されます。同期遅延しきい値は短時間の遅延を排除するので、「ユーザーロック時間」が「同期待ち時間」よりも大きくなる可能性があります。

待ち時間は、データ収集のオーバーヘッドによってひずみます。そして、このオーバーヘッドは、収集されたイベントの個数に比例します。オーバーヘッドに費やされた待ち時間の一部は、イベント記録しきい値を大きくすることによって最小にできます。

ハードウェアカウンタオーバーフローのプロファイリング

ハードウェアカウンタオーバーフローのプロファイルデータには、カウンタ ID とオーバーフロー値が含まれます。この値は、カウンタがオーバーフローするように設定されている値よりも大きくなることがあります。 これは、オーバーフローが発生して、そのイベントが記録されるまでの間に命令が実行されるためです。この値は特に、浮動小数点演算やキャッシュミスなどのカウンタよりも、ずっと頻繁に増分されるサイクルカウンタや命令カウンタの場合に大きくなる可能性があります。イベント記録時の遅延はまた、呼び出しスタックとともに記録されたプログラムカウンタのアドレスが正確にオーバーフローイベントに対応しないことを意味します。詳細は、「ハードウェアカウンタオーバーフローの関連付け」を参照してください。また、「トラップ」も参照してください。トラップおよびトラップハンドラは、ユーザーの CPU 時間とサイクルカウンタによって報告される時間の間の、大きな相違の原因になることがあります。

収集されるデータ量は、オーバーフロー値に依存します。選択した値が小さすぎると、次のような影響が出ることがあります。

値が大きすぎると、良好な統計情報を得るにはオーバーフローの発生が少なくすぎる可能性があります。最後のオーバーフローの発生後に生じたカウントは、コレクタ関数の collector_final_counters によるものです。この関数がカウント値のかなりの割合を占める場合は、オーバーフロー値が大きすぎます。

ヒープトレース

コレクタは、メモリーの割り当てと割り当て解除の関数である mallocreallocmemalignfree に割り込むことによって、これらの関数の呼び出しに関するトレースデータを記録します。メモリーを割り当てるときにこれらの関数を迂回するプログラムの場合、トレースデータは記録されません。別のメカニズムが使用されている Java メモリー管理では、トレースデータは記録されません。

トレース対象の関数は、さまざまなライブラリから読み込まれる可能性があります。パフォーマンスアナライザで表示されるデータは、読み込み対象の関数が属しているライブラリに依存することがあります。

短時間で大量のトレース対象関数を呼び出すプログラムの場合、プログラムの実行に要する時間が大幅に長くなることがあります。延びた時間は、トレースデータの記録に使用されます。

データ空間プロファイリング

データ空間プロファイルはキャッシュミスなどのメモリー関係のイベントの報告データを集めたもので、メモリー関係のイベントが発生する命令だけではなく、イベントを発生させるデータオブジェクト参照についても報告します。データ空間プロファイリングは、Linux システムでは利用できません。

データ空間プロファイリングを可能にするには、ターゲットは、-xhwcprof フラグと -xdebugformat=dwarf -g フラグを付けて SPARC アーキテクチャー用にコンパイルされた C プログラムである必要があります。さらに、収集されるデータは、メモリー関係のカウンタのハードウェアカウンタプロファイルでなければならず、カウンタ名の前にオプションの + 記号を付加する必要があります。パフォーマンスアナライザには、データ空間プロファイリング関係のタブとして、「データオブジェクト」タブと「データレイアウト」タブのほか、メモリーオブジェクト用の各種のタブが含まれています。

データ空間プロファイリングは、プロファイル間隔の前にプラス記号 (+) を付加することで、時間ベースのプロファイリングとともに実施できます。

MPI トレース

MPI トレースは、Solaris プラットフォームでのみ利用できます。MPI トレースは、MPI ライブラリ関数の呼び出しに関する情報を記録します。イベント固有のデータは、要求と許可 (トレース対象の呼び出しの始まりと終わり) の高分解能のタイムスタンプ、および送受信動作の数と送受信バイト数で構成されます。トレースは、MPI ライブラリの呼び出しに割り込むことによって行われます。割り込み関数は、データ送信の最適化に関する情報や送信エラーに関する情報を持たないので、提示される情報は、以降で説明する単純な形でのデータ送信を表しています。

受信バイト数は、MPI 関数の呼び出しで定義されるバッファーサイズです。実際に受信したバイト数は、割り込み関数には利用できません。

一部の「大域通信」関数は、ルートと呼ばれる単一の受信プロセスまたは単一の起点を持ちます。こういった関数のアカウンティングは、次のように行われます。

次の例は、アカウンティングの手順を示しています。これらの例における G は、グループのサイズです。

MPI_Bcast() の呼び出しの場合、

MPI_Allreduce() の呼び出しの場合、

MPI_Reduce_scatter() の呼び出しの場合、