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

印刷ビューの終了

更新: 2016 年 6 月
 
 

プログラム構造へのアドレスのマッピング

呼び出しスタックが処理され、PC 値に変換されたあと、アナライザは、これらの PC をプログラム内の共有オブジェクト、関数、ソース行、および逆アセンブリ行 (命令) にマップします。ここでは、これらのマッピングについて説明します。

プロセスイメージ

プログラムを実行すると、そのプログラムの実行可能ファイルからプロセスがインスタンス化されます。プロセスのアドレス空間には、実行可能な命令を表すテキストが存在する領域や、通常は実行されないデータが存在する領域などの多数の領域があります。通常、呼び出しスタックに記録される PC は、プログラムのいずれかのテキストセグメント内のアドレスに対応しています。

プロセスの先頭テキストセクションは、実行可能ファイルそのものから生成されます。そのほかのテキストセクションは、プロセスの開始時に実行可能ファイルとともに読み込まれたか、プロセスによって動的にロードされた共有オブジェクトに対応しています。呼び出しスタック内の PC は、呼び出しスタックの記録時に読み込まれた実行可能ファイルと共有オブジェクトに基づいて解決されます。 実行可能ファイルと共有オブジェクトは非常に似ているため、まとめてロードオブジェクトと呼ばれます。

共有オブジェクトはプログラムの実行の過程でロードされたり、アンロードされたりする場合があるため、実行のタイミングによって、PC が対応する関数が異なることがあります。また、共有オブジェクトが読み込み解除されたあとに別のアドレスに再度読み込まれた場合は、異なる時点で異なる複数の PC が同じ関数に対応することもあります。

ロードオブジェクトと関数

各ロードオブジェクトには、それが実行可能ファイルか共有オブジェクトかにかかわらず、コンパイラによって生成された命令を含むテキストセクション、データ用のデータセクション、および各種のシンボルテーブルが含まれています。すべてのロードオブジェクトには、そのオブジェクト内に、グローバルに認識されているすべての関数の名前とアドレスを指定する ELF シンボルテーブルが含まれている必要があります。-g オプションでコンパイルされたロードオブジェクトには、追加のシンボリック情報が含まれています。この情報は ELF シンボルテーブルを補足するものであり、グローバルではない関数に関する情報、関数が属するオブジェクトモジュールに関する追加情報、およびアドレスをソース行に関連付ける行番号情報を提供します。

関数」という用語は、ソースコードで記述された高度な操作を表す一連の命令を表します。 この用語は、Fortran で使用されるサブルーチンや、C++ および Java プログラミング言語で使用されるメソッドなどもカバーしています。関数はソースコード内で明確に記述され、その名前は通常、アドレスセットを表すシンボルテーブルに表示されます。プログラムカウンタがそのセットに含まれている場合、プログラムはその関数内で実行されています。

原則として、ロードオブジェクトのテキストセグメント内のアドレスはすべて、関数にマップできます。関数ロードオブジェクト内のアドレス ロードオブジェクト関数のアドレス呼び出しスタック上のリーフ PC およびほかのすべての PC で、まったく同じマッピング情報が使用されます。関数の多くは、プログラムのソースモデルに直接対応します。以降のセクションでは、これに該当しない関数について説明します。

別名を持つ関数

一般に、関数は大域関数として定義されます。このことは、プログラム内のあらゆる部分で関数名が既知であることを意味します。大域関数の名前は、実行可能ファイル内で一意である必要があります。アドレス空間内に同一名の大域関数が複数存在する場合、実行時リンカーはすべての参照をそのうちの 1 つに決定します。その他の関数は実行されず、関数リストにそれらの関数が含まれることはありません。「選択の詳細」ウィンドウでは、選択した関数を含む共有オブジェクトおよびオブジェクトモジュールを調べることができます。

さまざまな状況で、同じ関数が異なる名前で認識されることがあります。この一般的な例としては、コードの同一部分に対して、いわゆる弱いシンボルと強いシンボルが使用されている場合があります。一般に、強い名前は対応する弱い名前と同じですが、前にアンダースコアが付きます。スレッドライブラリ内の多くの関数には、強い名前、弱い名前、代替内部シンボルに加えて、pthread および Solaris スレッド用に別の名前があります。このようなすべてのケースで、パフォーマンスアナライザの「関数」ビューでは 1 つの名前だけが使用されます。選択される名前は、アドレスをアルファベット順に並べた最後のシンボルです。ほとんどの場合は、この名前がユーザーの使用する名前に対応しています。「選択の詳細」ウィンドウでは、選択した関数のすべての別名が表示されます。

一意でない関数名

    別名を持つ関数ではコードの同じ部分に対して複数の名前が反映されますが、状況によっては、コードの複数の部分が同じ名前を持つことがあります。

  • モジュール性を実現するために、関数が静的関数として定義されることがあります。これは、その関数名がプログラムの一部 (一般には、コンパイル済みの 1 つのオブジェクトモジュール) でだけ認識されることを意味します。このような場合、プログラムのまったく異なる部分を参照している同じ名前の複数の関数がパフォーマンスアナライザに表示されます。「選択の詳細」ウィンドウでは、こうした関数を区別するために、これら各関数のそれぞれにオブジェクトモジュール名が表示されます。また、こうした関数のどの名前が選択されたとしても、その関数のソース、逆アセンブリ、呼び出し元と呼び出し先を表示することができます。

  • ライブラリ関数の弱い名前を持つラッパー関数または割り込み関数がプログラムで使用され、そのライブラリ関数の呼び出しに優先されることがあります。一部のラッパー関数はライブラリ内の元の関数を呼び出しますが、その場合は、その名前の両方のインスタンスがパフォーマンスアナライザの関数リストに表示されます。こうした関数は、元の共有オブジェクトやオブジェクトモジュールが異なるため、それらの情報を基に区別することができます。コレクタは一部のライブラリ関数をラップしますが、ラッパー関数と実際の関数の両方がパフォーマンスアナライザに表示される場合があります。

ストリップ済み共有ライブラリの静的関数

ライブラリ内では、ライブラリ内部で使用される名前がユーザーが使用する名前と競合しないように、静的関数がよく使用されます。ライブラリをストリップすると、静的関数の名前はシンボルテーブルから削除されます。このような場合、パフォーマンスアナライザは、ストリップ済み静的関数を含むライブラリ内のテキスト領域ごとに擬似的な名前を生成します。この名前は <static>@0x12345 という形式で、@ 記号に続く文字列は、ライブラリ内のそのテキスト領域のオフセットを表します。パフォーマンスアナライザは、連続する複数のストリップ済み静的関数と単一のストリップ済み静的関数を区別できないため、2 つ以上のストリップ済み静的関数が、各メトリックがまとめられた状態で表示される場合があります。

ストリップ済み静的関数は、その PC が静的関数の保存命令のあとに表示されるリーフ PC である場合を除いて、正しい呼び出し元から呼び出されたように表示されます。シンボリック情報がない場合、パフォーマンスアナライザは保存アドレスを認識しないため、復帰レジスタを呼び出し元として使用するかどうかを判断できません。復帰レジスタは常に無視されます。複数の関数が 1 つの <static>@0x12345 関数にまとめられることがあるため、実際の呼び出し元または呼び出し先が隣接する関数と区別されないことがあります。

Fortran での代替エントリポイント

Fortran には、コードの 1 つの部分を指す複数のエントリを保持し、呼び出し元が関数の途中を呼び出せるようにする方法が用意されています。このようなコードをコンパイルしたときに生成されるコードは、メインのエントリポイントの導入部、代替エントリポイントの導入部、関数のコード本体で構成されます。各導入部では、関数があとで復帰するためのスタックが作成され、そのあとで、コード本体に分岐または接続します。

各エントリポイントの導入部のコードは、そのエントリポイント名を持つテキスト領域に常に対応しますが、サブルーチン本体のコードは、エントリポイント名の 1 つだけを受け取ります。受け取る名前は、コンパイラによって異なります。

導入部にそれほど長い時間がかかることはなく、サブルーチンの本体に関連付けられているエントリポイント以外のエントリポイントに対応する関数がパフォーマンスアナライザに表示されることもほとんどありません。通常、代替エントリポイントを含む Fortran サブルーチン内の時間を表す呼び出しスタックは、導入部ではなく、サブルーチンの本体に PC があるため、本体に関連付けられた名前だけが呼び出し先として表示されます。同様に、そうしたサブルーチンからのあらゆる呼び出しは、サブルーチン本体に関連付けられている名前から行われたものとみなされます。

クローン関数

コンパイラは、通常以上の最適化が可能な関数への呼び出しを見分けることができます。こういった呼び出しの一例としては、引数の一部が定数である関数への呼び出しが挙げられます。 コンパイラは、最適化できる特定の呼び出しを識別すると、クローンと呼ばれるこの関数のコピーを作成し、最適化されたコードを生成します。クローン関数名は、特定の呼び出しを識別する、符号化された名前です。パフォーマンスアナライザはこの名前を復号化し、クローン関数の各インスタンスを関数リスト内に個別に表示します。 各クローン関数は異なる命令セットを持っているため、注釈付き逆アセンブリリストにはクローン関数が個別に表示されます。各クローン関数のソースコードは同じであるため、注釈付きソースリストでは関数のすべてのコピーについてデータが集計されます。

インライン関数

インライン関数とは、実際の呼び出しの代わりに、コンパイラが生成した命令がその関数の呼び出し位置に挿入される関数のことです。インライン化には、C++ のインライン関数定義と明示的または自動的なインライン化の 2 種類があり、どちらもパフォーマンスを向上させるために実行され、またどちらもパフォーマンスアナライザに影響を与えます。

  • C++ のインライン関数定義。このようにインライン化する理由は、関数呼び出しのコストがインライン関数によって行われる作業よりはるかに大きいためです。関数呼び出しを設定するより、関数のコードを呼び出し位置に挿入する方が優れています。一般に、アクセス関数は、必要な命令が 1 つだけであることが多いため、インライン化対象として定義されます。–g オプションでコンパイルすると、関数のインライン化は無効になります。–g0 でコンパイルすると、関数のインライン化が許可されます。この方法が推奨されます。

  • 高レベルの最適化 (4 および 5) で行われる明示的または自動的なインライン化。明示的および自動的なインライン化は、–g が有効なときにも行われます。この種のインライン化を行うのは、関数呼び出しの時間を節約するための場合もあります。しかし、多くの場合は、レジスタの利用や命令の実行スケジューリングを最適化できる命令数を増やすためです。

いずれのインライン化も、メトリックの表示に同じ影響を及ぼします。ソースコードに記述されていて、インライン化された関数は、関数リストにも、また、そうした関数のインライン化先の関数の呼び出し先としても表示されません。通常ならば、インライン化された関数の呼び出し位置で包括的メトリックとみなされるメトリック (呼び出された関数で費やされた時間を表す) が、実際には呼び出し位置 (インライン化された関数の命令を表す) が原因の排他的メトリックと報告されます。


注 -  インライン化によってデータの解釈が難しくなることがあります。このため、パフォーマンス解析のためにプログラムをコンパイルするときには、インライン化を無効にすることを推奨します。C++ アクセス関数のインライン化は、パフォーマンスコストの上昇を避けるため、無効にすべきではありません。

場合によっては、関数がインライン化されている場合も、いわゆるライン外関数が残されます。一部の呼び出し側ではライン外関数が呼び出され、それ以外では命令がインライン化されます。このような場合、関数は関数リストに含まれますが、関連するメトリックはライン外呼び出しだけを表します。

コンパイラ生成の本体関数

関数内のループまたは並列化ディレクティブが指定された領域を並列化する場合、コンパイラは、元のソースコードには含まれていない新しい本体関数を作成します。こうした関数については、OpenMP実行の概要 で説明しています。

ユーザーモードでは、パフォーマンスアナライザはこれらの関数を表示しません。上級モードとマシンモードでは、パフォーマンスアナライザはこれらの関数を通常の関数として表示し、コンパイラで生成される名前に加えて、抽出元の関数に基づいて名前を割り当てます。これらの関数の排他的メトリックおよび包括的メトリックは、本体関数で費やされる時間を表します。また、構造の抽出元の関数は各本体関数の包括的メトリックを示します。このプロセスは、OpenMP実行の概要 で説明しています。

並列ループを含む関数をインライン化した場合、そのコンパイラ生成の本体関数名には、元の関数ではなく、インライン化先の関数の名前が反映されます。


注 -  コンパイラ生成の本体関数の名前は、–g でコンパイルされたモジュールでのみ復号化できます。

アウトライン関数

アウトライン関数は、フィードバック最適化コンパイル中に作成される場合があります。それらは、通常では実行されないコード、特に、最終的な最適化コンパイル用のフィードバックの生成に使用される「試験実行」の際に実行されないコードを表しています。一般的な例は、ライブラリ関数の戻り値でエラーチェックを実行するコードです。通常、エラー処理コードは実行されません。ページングや命令キャッシュの動作を向上させるため、このようなコードはアドレス空間内の別の場所に移動され、別の関数となります。アウトライン関数の名前は、コードの取り出し元関数の名前や特定のソースコードセクションの先頭の行番号を含む、アウトライン化したコードのセクションに関する情報をエンコードします。これらの符号化された名前は、リリースごとに異なります。パフォーマンスアナライザは、読みやすいバージョンの関数名を表示します。

アウトライン関数は、実際には呼び出されず、ジャンプ先になります。同様に、復帰はせず、ジャンプで戻ります。動作をユーザーのソースコードモデルにより近づけるため、パフォーマンスアナライザは、main 関数からそのアウトライン部分への擬似的な呼び出しを生成します。

アウトライン関数は、適切な包括的および排他的メトリックを持つ通常の関数として表示されます。また、アウトライン関数のメトリックは、アウトライン化元の関数の包括的メトリックとして追加されます。

フィードバック最適化コンパイルの詳細は、次のいずれかのマニュアルにある –xprofile コンパイラオプションの説明を参照してください。

動的にコンパイルされる関数

動的にコンパイルされる関数は、プログラムの実行中にコンパイルおよびリンクされる関数です。コレクタ API 関数を使用して必要な情報をユーザーが提供しないかぎり、コレクタは C や C++ で記述された動的にコンパイルされる関数に関する情報を把握できません。API 関数については、動的な関数とモジュール を参照してください。情報を提供しなかった場合、関数は <Unknown> としてパフォーマンス解析ツールに表示されます。

Java プログラムの場合は、コレクタが、Java HotSpot 仮想マシンによってコンパイルされるメソッドに関する情報を取得します。API 関数を使用して情報を指定する必要はありません。ほかのメソッドの場合、パフォーマンスツールはメソッドを実行する JVM ソフトウェアの情報を表示します。Java ユーザー表現では、すべてのメソッドがインタプリタされたバージョンとマージされます。マシン表現では、HotSpot でコンパイルされたバージョンが個別に表示され、JVM 関数はインタプリタされたメソッドごとに表示されます。

<Unknown> 関数

状況によっては、PC が既知の関数にマップされないことがあります。このような場合、その PC は <Unknown> という名前の特殊関数にマップされます。

    次の状況は、PC の <Unknown> へのマッピングを示しています。

  • C または C++ で記述された関数が動的に生成され、この関数に関する情報がコレクタ API 関数によってコレクタに提供されない場合。コレクタ API 関数の詳細については、動的な関数とモジュール参照してください。

  • Java メソッドが動的にコンパイルされるが、Java プロファイリングが無効である場合。

  • PC が実行可能ファイルまたは共有オブジェクトのデータセクション内のアドレスに対応している場合。たとえば、そのデータセクション (.mul.div など) 内に複数の関数が存在する SPARC V7 バージョンの libc.so があります。コードがデータセクションにあるため、SPARC V8 または SPARC V9 プラットフォームで動作していることをライブラリが検出したときに、動的に書き換えてマシン命令を利用できるようになります。

  • 実験ファイルに記録されない実行可能ファイルのアドレス空間内の共有オブジェクトに PC が対応する場合。

  • PC が既知のロードオブジェクト内に存在しない場合。もっとも考えられる原因は、展開に失敗して、PC 値として記録された値が PC ではなく、別のワードである場合です。PC が復帰レジスタのとき、既知のロードオブジェクト内に存在しないように見える場合は、<Unknown> 関数に割り当てられずに無視されます。

  • コレクタにシンボリック情報がない JVM ソフトウェアの内部部分に PC がマップしている場合。

<Unknown> 関数の呼び出し元と呼び出し先は呼び出しスタック内の前の PC と次の PC を表し、通常どおり処理されます。

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 セクションに入る順番を待っているスレッド

<JVM-System> 関数

ユーザー表現では、<JVM-System> 関数は、Java プログラムの実行以外のアクションを実行している JVM ソフトウェアによって使用された時間を表します。JVM ソフトウェアは、ガベージコレクションや HotSpot コンパイルなどのタスクを、この時間間隔内で実行します。ユーザーモードでは、<JVM-System> は関数リストに表示されます。

<no Java callstack recorded> 関数

<no Java callstack recorded> 関数は、Java スレッドが Java ユーザー表現のみである点を除き、<Unknown> 関数と同じです。コレクタが Java スレッドからイベントを受信すると、ネイティブスタックを展開し、JVM ソフトウェアを呼び出して対応する Java スタックを取得します。その呼び出しが何らかの理由で失敗した場合は、そのイベントが擬似関数 <no Java callstack recorded> とともにパフォーマンスアナライザに表示されます。JVM ソフトウェアは、デッドロックを回避するためか、Java スタックを展開すると過剰な同期化が発生するときに呼び出しスタックの報告を拒否することがあります。

<Truncated-stack> 関数

呼び出しスタック内の個々の関数のメトリックを記録するため、パフォーマンスアナライザによって使用されるバッファーのサイズは制限されています。呼び出しスタックのサイズが大きくなってバッファーがいっぱいになった場合、呼び出しスタックのサイズがそれ以上増えると、パフォーマンスアナライザが関数のプロファイル情報を強制的にドロップします。ほとんどのプログラムでは排他的 CPU 時間の大部分がリーフ関数で費やされるため、パフォーマンスアナライザは、エントリ関数 _start()main() から始めて、スタックの下部にあるもっとも重要性の低い関数のメトリックをドロップします。ドロップされた関数のメトリックは、1 つの擬似関数 <Truncated-stack> に統合されます。また、<Truncated-stack> 関数は Java プログラムにも表示される場合があります。

より深いスタックをサポートするには、SP_COLLECTOR_STACKBUFSZ 環境変数の数値を増やして設定します。

<Total> 関数

<Total> 関数は、プログラム全体を表すために使用される擬似的な構成概念です。すべてのパフォーマンスメトリックは、呼び出しスタック上の関数に帰属するのに加えて、特殊関数 <Total> に帰属します。この関数は関数リストの先頭に表示され、そのデータを使用してほかの関数のデータの概略を見ることができます。「呼び出し元-呼び出し先」リストでは、この関数は、任意のプログラムの実行のメインスレッドにおける _start() の名目上の呼び出し元として、また作成されたスレッドの _thread_start() の名目上の呼び出し元としても表示されます。スタックの展開が不完全であった場合、<Total> 関数は、<Truncated-stack> の呼び出し元として表示される可能性があります。

ハードウェアカウンタオーバーフロープロファイルに関連する関数

    次の関数は、ハードウェアカウンタオーバーフロープロファイルに関連します。

  • collector_not_program_related – カウンタはプログラムに関連しません。

  • collector_hwcs_out_of_range – カウンタは、オーバーフローシグナルを生成せずにオーバーフロー値を超えたように見えます。値が記録され、カウンタがリセットされます。

  • collector_hwcs_frozen – カウンタは、オーバーフロー値を超えて停止されたように見えますが、オーバーフローシグナルが消失したように見えます。値が記録され、カウンタがリセットされます。

  • collector_hwc_ABORT – 通常は特権付きプロセスがカウンタの制御を取得したときに、ハードウェアカウンタの読み取りに失敗したため、ハードウェアカウンタの収集が終了しました。

  • collector_record_counter – ハードウェアカウンタイベントの処理および記録中に累積されたカウントであり、ハードウェアカウンタオーバーフロープロファイリングのオーバーヘッドの一部を占めます。この値が <Total> カウントの大きな部分に対応している場合は、オーバーフローの間隔を長くする (つまり、分解能の構成を小さくする) ことが推奨されます。