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

第 7 章 注釈付きソースと逆アセンブリデータについて

注釈付きソースコードと注釈付き逆アセンブリコードは、関数内のパフォーマンス低下の原因になっているソース行または命令を特定するのに役立ち、コンパイラがコードに対して行なった変換処理に関するコメントを表示します。ここでは、注釈の処理と、注釈付きコードを解釈する上での問題点をいくつか説明します。

この章では、次の内容について説明します。

注釈付きソースコード

実験の注釈付きソースコードは、パフォーマンスアナライザで「アナライザ」ウィンドウの左の区画にある「ソース」タブを選択することで表示できます。または、実験を実行しなくても、er_src ユーティリティーを使用して注釈付きソースコードを表示できます。ここでは、パフォーマンスアナライザでソースコードを表示する方法について説明します。er_src ユーティリティーを使用した注釈付きソースコードの表示の詳細は、「実験なしのソース/逆アセンブリの表示」を参照してください。

アナライザの注釈付きソースには、次の情報が含まれています。

パフォーマンスアナライザの「ソース」タブのレイアウト

「ソース」タブは、いくつかの列に分かれています。ウィンドウの左側には個々のメトリックスを表示する固定幅の列が表示されます。右側の残りの列には、注釈付きソースが表示されます。

元のソース行の識別

注釈付きソースで黒字で表示されるすべての行は、元のソースファイルから取得されたものです。注釈付きソース列の各行の先頭の番号は、元のソースファイルの行番号に対応します。別の色で文字が表示されている行は、インデックス行かコンパイラのコメント行です。

「ソース」タブのインデックス行

ソースファイルとは、オブジェクトファイルを生成するためにコンパイルされるか、またはバイトコードに解釈されるファイルのことです。オブジェクトファイルには通常、ソースコード内の関数、サブルーチン、またはメソッドに対応する実行可能コードの領域が 1 つ以上含まれています。アナライザはオブジェクトファイルを解析して、それぞれの実行可能領域を関数として識別します。そして、オブジェクトコード内で見つけた関数を、オブジェクトコードに関連付けられたソースファイル内の関数、ルーチン、サブルーチン、またはメソッドにマップしようとします。アナライザは解析が成功すると、注釈付きソースファイル内の、オブジェクトコードで検出された関数の最初の命令に対応する場所に、インデックス行を追加します。

注釈付きソースは、「関数」タブのリストにインライン関数が表示されていない場合でも、インライン関数を含むすべての関数のインデックス行を表示します。「ソース」タブには、インデックス行が赤いイタリック体で、テキストが山括弧内に示されます。もっとも単純な種類のインデックス行は、関数のデフォルトコンテキストに対応しています。関数のデフォルトソースコンテキストは、その関数の最初の命令が帰するソースファイルとして定義されます。次の例は、C 関数 icputime のインデックス行を示しています。


                    578. int
                    579. icputime(int k)
0.       0.         580. {
                         <Function: icputime>

この例で分かるように、インデックス行は最初の命令に続く行に表示されます。C ソースの場合は、最初の命令は、関数本体の先頭が開く括弧に対応します。Fortran ソースの場合は、各サブルーチンのインデックス行が、subroutine キーワードを含む行に続きます。また、次の例に示すように、main 関数のインデックス行が、アプリケーションの起動時に実行される最初の Fortran ソース命令に続きます。


    	     	     	     	1. ! Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
    	     	     	     	2. ! @(#)omptest.f 1.11 10/03/24 SMI
    	     	     	     	3. ! Synthetic f90 program, used for testing openmp directives and the
    	     	     	     	4. !       analyzer
    	     	     	     	5. 
0.   	0.   	0.   	0.  	6.        program omptest
                                      <Function: MAIN>  
                                   7.
                                   8. !$PRAGMA C (gethrtime, gethrvtime)

場合によっては、アナライザは、オブジェクトコード内に見つけた関数を、そのオブジェクトコードに関連付けられたソースファイル内のプログラミング命令を使ってマップできないことがあります。たとえば、ヘッダーファイルのように、コードが #include されている場合や、ほかのファイルからインライン化されている場合があります。

また、特別なインデックス行やコンパイラのコメントではない特殊な行は、赤で示されます。たとえば、コンパイラの最適化の結果、ソースファイルに記述されているコードに対応しないオブジェクトコード内の関数について、特別なインデックス行が作成される場合があります。詳細は、「「ソース」タブ、「逆アセンブリ」タブ、「PC」タブの特別な行」を参照してください。

コンパイラのコメント

コンパイラのコメントは、コンパイラによって最適化されたコードがどのように生成されたかを示します。インデックス行や元のソース行と区別できるように、コンパイラのコメント行は青く表示されます。コンパイラのさまざまな段階で、実行可能ファイルにコメントが挿入されることがあります。各コメントは、ソースの特定の行に関連付けられます。注釈付きソースの書き込み時には、ソース行に対してコンパイラが生成するコメントが、ソース行の直前に挿入されます。

コンパイラのコメントは、最適化するためにソースコードに対して行われた変換の大部分に関する情報を提供します。こうした変換には、ループの最適化、並列化、インライン化、パイプライン化などがあります。次に、コンパイラのコメントの例を示します。


0.   	0.   	0.   	0.   	 28.       SUBROUTINE dgemv_g2 (transa, m, n, alpha, b, ldb,   &
     	     	     	     	 29.      &                   c, incc, beta, a, inca)
     	     	     	     	 30.       CHARACTER (KIND=1) :: transa
                                	 31.       INTEGER   (KIND=4) :: m, n, incc, inca, ldb
                                	 32.       REAL      (KIND=8) :: alpha, beta
                                	 33.       REAL      (KIND=8) :: a(1:m), b(1:ldb,1:n), c(1:n)
                                	 34.       INTEGER            :: i, j
                                	 35.       REAL      (KIND=8) :: tmr, wtime, tmrend
                                	 36.       COMMON/timer/ tmr
                                	 37. 
                             Function wtime_ not inlined because the compiler has not seen 
                             the body of the routine
0.   	0.   	0.   	0.   	 38.       tmrend = tmr + wtime()


                             Function wtime_ not inlined because the compiler has not seen 
                             the body of the routine
                             Discovered loop below has tag L16
0.   	0.   	0.   	0.   	 39.       DO WHILE(wtime() < tmrend)
                          	
                         	Array statement below generated loop L4
0.   	0.   	0.   	0.   	 40.       a(1:m) = 0.0
                                	 41. 
     	     	     	     	
                         	Source loop below has tag L6
0.   	0.   	0.   	0.   	 42.       DO j = 1, n       ! <=-----\ swapped loop indices
   	
                         	Source loop below has tag L5
                             L5 cloned for unrolling-epilog.  Clone is L19
                             All 8 copies of L19 are fused together as part of unroll and jam
                             L19 scheduled with steady-state cycle count = 9
                             L19 unrolled 4 times
                             L19 has 9 loads, 1 stores, 8 prefetches, 8 FPadds, 
                             8 FPmuls, and 0 FPdivs per iteration
                             L19 has 0 int-loads, 0 int-stores, 11 alu-ops, 0 muls, 
                             0 int-divs and 0 shifts per iteration
                             L5 scheduled with steady-state cycle count = 2
                             L5 unrolled 4 times
                             L5 has 2 loads, 1 stores, 1 prefetches, 1 FPadds, 1 FPmuls,
                             and 0 FPdivs per iteration
                             L5 has 0 int-loads, 0 int-stores, 4 alu-ops, 0 muls, 
                             0 int-divs and 0 shifts per iteration
0.210	0.210	0.210	0.   	 43.          DO i = 1, m    
4.003	4.003	4.003	0.050	 44.             a(i) = a(i) + b(i,j) * c(j)
0.240	0.240	0.240	0.   	 45.          END DO  
0.   	0.   	0.   	0.   	 46.       END DO  
                              	   47.       END DO
                         	        48. 
0.   	0.   	0.   	0.   	 49.       RETURN
0.   	0.   	0.   	0.   	 50.       END

「ソース」タブに表示されるコンパイラコメントの種類は、「データ表示方法の設定」ダイアログボックスの「ソース/逆アセンブリ」タブを使って設定できます。詳細は、「データ表示オプションの設定」を参照してください。

共通部分式の削除

非常に一般的な最適化の例として、1 つの式が複数の場所に存在するときに、この式のコードを 1 つの場所にまとめることによってパフォーマンスを向上させることができます。たとえば、コードブロックの ifelse の分岐の両方で同じ演算が記述されている場合、コンパイラはその演算を if 文の直前に移動することができます。実際にそのようにした場合、コンパイラは以前出現した一方の式に基づいて、命令に行番号を割り当てます。割り当てられた行番号が if 構造の分岐の 1 つに対応していて、実際にはもう一方の分岐が常に実行される場合、注釈付きソースでは、実行されない分岐内の行のメトリックスが表示されます。

ループの最適化

コンパイラは、数種類のループの最適化を行うことができます。一般的なものを次に示します。

ループの展開では、ループ本体内でループを数回反復し、それに応じてループインデックスを調整します。ループの本体が大きくなるほど、コンパイラはより効率的に命令をスケジュールできます。また、ループインデックスの増分や条件検査操作によるオーバーヘッドが減少します。残りのループは、ループのピーリングを使って処理されます。

ループのピーリングでは、ループから多数のループの反復を取り除き、これらをループの前か後ろに適宜移動します。

ループの入れ換えは、メモリーのストライドを最小限に抑えてキャッシュ行のヒット率を最大限に上げるために、入れ子のループの順序を変更します。

ループの融合は、隣り合ったループや近接したループを 1 つのループにまとめます。ループの融合からは、ループの展開と同じような利点がもたらされます。さらに、最適化済みの 2 つのループで共通のデータにアクセスする場合は、ループの融合によってループのキャッシュの局所性が改善されて、コンパイラは命令レベルの並列化機能をさらに活用することが可能になります。

ループの分散はループの融合の反対で、ループは複数のループに分割されます。この最適化は、ループ内の計算回数が過度に多くなり、パフォーマンス低下の原因となるレジスタのスピルが発生する場合に適しています。また、ループの分裂は、ループに条件文が含まれている場合にも有効です。場合によっては、条件文を含むものと含まないものの 2 つにループを分割できます。これによって、条件文を含まないループにおけるソフトウェアのパイプライン化の機会が増えます。

場合によって、入れ子のループでは、コンパイラはループの分裂を適用してループを分割し、次にループの融合を実行して異なる方法でループをまとめ直すことでパフォーマンスを改善します。この場合は、次のようなコンパイラのコメントが表示されます。


    Loop below fissioned into 2 loops
    Loop below fused with loop on line 116
    [116]    for (i=0;i<nvtxs;i++) {

関数のインライン化

インライン関数を使用して、コンパイラは、実際の関数呼び出しを行う代わりに、関数が呼び出されている場所に関数の命令を直接挿入します。つまり、C/C++ マクロと同様に、それぞれの呼び出し場所でインライン関数の命令の複製が作成されます。コンパイラは、高レベルの最適化 (4 および 5) で明示的または自動的なインライン化を実行します。インライン化によって関数呼び出しの負荷が減り、レジスタの使用や命令のスケジュールを最適化するための命令がさらに提供されますが、その代わりに、コードのメモリー使用量が多くなります。次に、コンパイラコメントのインライン化の例を示します。


                Function initgraph inlined from source file ptralias.c 
                    into the code for the following line
0.       0.         44.       initgraph(rows);

注 –

コンパイラのコメントは、アナライザの「ソース」タブ内で 2 行にまたがって折り返されることはありません。


並列化

Sun、Cray、または OpenMP の並列化指令が含まれているコードの場合、複数プロセッサ上での並列実行用にコンパイルできます。コンパイラのコメントは、並列化が実行されている場所と実行されていない場所、およびその理由を示します。次に、並列化コンピュータのコメントの例を示します。


0.       6.324       9. c$omp  parallel do shared(a,b,c,n) private(i,j,k)
                   Loop below parallelized by explicit user directive
                   Loop below interchanged with loop on line 12
0.010    0.010     [10]            do i = 2, n-1

                   Loop below not parallelized because it was nested in a parallel loop
                   Loop below interchanged with loop on line 12
0.170    0.170      11.               do j = 2, i

並列実行とコンパイラ生成の本体関数の詳細は、「OpenMP ソフトウェア実行の概要」を参照してください。

注釈付きソースの特別な行

「ソース」タブには、特殊な場合のためのほかの注釈を表示できます。これらの注釈は、コンパイラのコメントの形で、またはインデックス行と同じ色で特別な行に表示できます。詳細は、「「ソース」タブ、「逆アセンブリ」タブ、「PC」タブの特別な行」を参照してください。

ソース行メトリックス

実行可能コードの各行のソースコードメトリックスは、固定幅の列に表示されます。メトリックスは、関数リストのものと同じです。実験のデフォルト値は、.er.rc ファイルを使って変更できます。詳細は、「デフォルト値を設定するコマンド」を参照してください。また、表示されるメトリックスとしきい値の強調表示も、「データ表示方法の設定」ダイアログボックスを使ってアナライザで変更できます。詳細は、「データ表示オプションの設定」を参照してください。

注釈付きソースコードは、ソース行レベルでのアプリケーションのメトリックスを示します。注釈付きソースは、アプリケーションの呼び出しスタックに記録された PC (プログラムカウント) を読み取り、各 PC をソース行にマッピングすることによって作成されます。注釈付きソースファイルを作成するために、アナライザは、最初に特定のオブジェクトモジュール (.o ファイル) またはロードオブジェクト内に生成されたすべての関数を特定し、各関数のすべての PC のデータをスキャンします。注釈付きソースを作成するには、アナライザが、すべてのオブジェクトモジュールまたはロードオブジェクトを検出して読み取り、PC からソース行へのマッピングを特定できる必要があります。また、表示するソースファイルを読み取って、注釈付きのコピーを作成できる必要もあります。アナライザはソースファイル、オブジェクトファイル、および実行可能ファイルを次のデフォルトの場所で順に検索し、正しいベース名のファイルが見つかると検索を停止します。

デフォルト値は、addpath または setpath 指令により、またはアナライザの「データ表示方法の設定」ダイアログボックスで変更できます。

addpath または setpath によって設定されたパスのリストを使用してファイルを検出できない場合は、pathmap コマンドを使用して 1 つ以上のパス再マッピングを指定できます。pathmap コマンドでは、old-prefixnew-prefix を指定できます。ソースファイル、オブジェクトファイル、または共有オブジェクトのパス名が old-prefix で指定した接頭辞で始まる場合、古い接頭辞は new-prefix で指定した接頭辞に置き換えられます。結果のパスは、ファイルの検索に使用されます。複数の pathmap コマンドが提供されており、それぞれが、ファイルが見つかるまで試行されます。

コンパイル処理では、要求される最適化レベルに応じて多くの段階があり、変換によって命令とソース行のマッピングに混乱が生じることがあります。最適化によっては、ソース行の情報が完全に失われたり、混乱が生じたりすることがあります。コンパイラは、さまざまな発見手法によって命令のソース行を追跡しますが、こうした手法は絶対ではありません。

ソース行メトリックスの解釈

命令のメトリックスについては、実行対象の命令を待っている間に発生したメトリックスとして解釈する必要があります。イベントが記録されるときに実行中である命令がリーフ PC と同じソース行に存在している場合、メトリックスはこのソース行を実行した結果であると解釈できます。ただし、実行中の命令とリーフ PC が存在しているソース行がそれぞれ異なる場合、リーフ PC が存在しているソース行のメトリックスの少なくとも一部は、実行中命令のソース行が実行待ちしていた間に集計されたメトリックスであると解釈する必要があります。この一例としては、1 つのソース行で計算された値が次のソース行で使用される場合が挙げられます。

メトリックスの解釈方法がもっとも問題となるのは、キャッシュミスやリソース待ち行列ストールなど、実行が大幅に遅延している場合や、命令が直前の命令の結果を待っている場合です。このような場合、ソース行のメトリックスが異常に高く見えることがあります。コード内のほかのソース行を調べて、こういった高メトリック値の原因である行を突きとめてください。

メトリックの形式

表 7–1 に、注釈付きソースコードの行に表示可能な 4 種類のメトリックスを示します。

表 7–1 注釈付きソースコードのメトリックス

メトリック 

意味 

(空白) 

プログラムに、このコード行に対応する PC が存在しません。コメント行は常にこの空白になります。また、次の場合の見かけ上のコード行も空白になります。 

  • 最適化中に、見かけ上のコード部分のすべての命令が削除されている。

  • コードが別の場所で繰り返されていて、コンパイラによって共通する部分式が認識され、その行のすべての命令に繰り返し部分の行番号が付けられている。

  • コンパイラによって、その行の命令に不正な行番号が付けられている。

0.

プログラム内のいくつかの PC がこの行から派生したものとしてタグ付けされていますが、それらの PC を参照しているデータがありません。統計的にサンプリングされたかトレースされた呼び出しスタックに、そのような PC は存在しません。0. メトリックは、その行が実行されなかったことを意味するのではなく、プロファイリングデータパケットや記録されたトレースデータパケットに統計として表示されなかったことだけを意味します。

0.000

この行の少なくとも 1 つの PC がデータに表れていますが、メトリック値の計算でゼロに丸められました。 

1.234

この行に属するすべての PC のメトリックスの合計が、表示されているゼロ以外の数値になりました。 

注釈付き逆アセンブリコード

注釈付き逆アセンブリは、関数またはオブジェクトモジュールの命令のアセンブリコードのリストを提供します。このリストには、各命令に関連付けられているパフォーマンスメトリックスが含まれています。注釈付き逆アセンブリは複数の方法で表示することができ、どの方法で表示されるかは、行番号のマッピング情報およびソースファイルが存在するかどうか、また注釈付き逆アセンブリが要求されている関数のオブジェクトモジュールが既知かどうかによって決まります。

逆アセンブリコードの各命令には、注釈として次の情報が付けられます。

呼び出しアドレスの解決が可能な場合、それらのアドレスは関数名などのシンボルに変換されます。メトリックスは、命令行について表示されます。対応する表示方式が設定されていれば、インタリーブされるソースコードについても表示することができます。表示可能なメトリック値は、表 7–1 に示されているソースコードの注釈で説明しているとおりです。

複数の場所で #include が指定されているコードの逆アセンブリリストでは、そのコードが #include されている回数だけ逆アセンブリ命令が繰り返されます。ソースコードは、ファイル内で最初に出現する逆アセンブリコードの繰り返しブロックのみインタリーブされます。たとえば、inc_body.h というヘッダーに定義されているコードブロックが、inc_bodyinc_entryinc_middle、および inc_exit という 4 つの関数によって #include されている場合、逆アセンブリ命令のブロックは inc_body.h の逆アセンブリリストに 4 回現れますが、ソースコードは逆アセンブリ命令の 4 つのブロックの最初のもののみインタリーブされます。「ソース」タブに切り替えると、逆アセンブリコードの繰り返しにそれぞれ対応するインデックス行が表示されます。

インデックス行は「逆アセンブリ」タブ内に表示される場合があります。「ソース」タブの場合とは異なり、これらのインデックス行を直接ナビゲーションの目的に使用することはできません。ただし、インデックス行の直下の命令の 1 つにカーソルを置いて「ソース」タブを選択すると、インデックス行で参照されているファイルに移動できます。

ほかのファイルのコードを #include するファイルでは、ソースコードのインタリーブなしで、インクルードされたコードが逆アセンブリ命令として表示されます。ただし、これらの命令の 1 つにカーソルを置いて「ソース」タブを選択すると、#include されているコードを含むファイルが開かれます。このファイルを表示した状態で「逆アセンブリ」タブを選択すると、インタリーブされたソースコードとともに逆アセンブリコードが表示されます。

インライン関数の場合はソースコードと逆アセンブリコードをインタリーブできますが、マクロの場合はできません。

コードが最適化されていない場合、各命令の行番号は逐次順であり、ソース行と逆アセンブリされた命令は予想どおりにインタリーブされます。最適化されている場合は、あとの行の命令が前の行の命令よりも前に表示されることがあります。アナライザのインタリーブアルゴリズムでは、命令が行 N にあると表示される場合は、常に、行 N とその行までのすべてのソース行がその命令の前に挿入されます。最適化を行なった結果、制御転送命令とその遅延スロット命令の間にソースコードが現れます。ソース行の N に関連するコンパイラのコメントは、その行の直前に挿入されます。

注釈付き逆アセンブリの解釈

注釈付き逆アセンブリを解釈するのは簡単ではありません。 リーフ PC とは、次に実行する命令のアドレスです。このため、命令の属性メトリックスは、命令の実行待ちに費やされた時間とみなされます。ただし、命令の実行は必ずしも順に行われるわけではなく、呼び出しスタックの記録に遅延があることもあります。注釈付き逆アセンブリコードを利用するには、実験の記録先であるハードウェアと、そのハードウェアが命令を読み込み、実行する方法を理解しておいてください。

次では、注釈付き逆アセンブリコードを解釈する上での問題点をいくつか説明します。

命令発行時のグループ化

命令は、命令発行グループと呼ばれるグループ単位で読み込まれ、発行されます。グループに含まれる命令は、ハードウェア、命令の種類、すでに実行された命令、およびほかの命令またはレジスタの依存関係によって異なります。その結果、ある命令が常に前の命令と同じクロックで実行され、次に実行される命令として現れない場合、その命令の出現回数は実際よりも少なくなることを意味します。また、呼び出しスタックが記録されるときに、「次」に実行する命令が複数存在する可能性もあります。

命令発行規則はプロセッサの種類ごとに異なり、キャッシュ行内の命令位置合わせに依存します。リンカーはキャッシュ行よりも高い精度による命令位置合わせを強制的に行うので、関連性がないと思える関数を変更すると、異なる命令の位置合わせが生じる可能性があります。位置合わせが異なると、パフォーマンスの向上や劣化が発生することがあります。

次の例では、同じ関数をわずかに異なる状況でコンパイルしてリンクしています。2 つの出力例は、er_print ユーティリティーからの注釈付き逆アセンブリリストを示しています。2 つの例の命令は同じですが、位置合わせが異なっています。

この例の命令位置合わせでは、cmpbl,a の 2 つの命令を別々のキャッシュ行にマップし、この 2 つの命令の実行待ちに多大な時間が消費されます。


   Excl.     Incl.
User CPU  User CPU
    sec.      sec.
                             1. static int
                             2. ifunc()
                             3. {
                             4.     int i;
                             5.
                             6.     for (i=0; i<10000; i++)
                                <function: ifunc>
   0.010     0.010              [ 6]    1066c:  clr         %o0
   0.        0.                 [ 6]    10670:  sethi       %hi(0x2400), %o5
   0.        0.                 [ 6]    10674:  inc         784, %o5
                             7.         i++;
   0.        0.                 [ 7]    10678:  inc         2, %o0
## 1.360     1.360              [ 7]    1067c:  cmp         %o0, %o5
## 1.510     1.510              [ 7]    10680:  bl,a        0x1067c
   0.        0.                 [ 7]    10684:  inc         2, %o0
   0.        0.                 [ 7]    10688:  retl
   0.        0.                 [ 7]    1068c:  nop
                             8.     return i;
                             9. }

この例の命令位置合わせでは、cmpbl,a の 2 つの命令を 1 つのキャッシュ行にマップし、この 2 つの命令の内 1 つの命令のみの実行待ちに多大な時間が消費されます。


   Excl.     Incl.
User CPU  User CPU
    sec.      sec.
                             1. static int
                             2. ifunc()
                             3. {
                             4.     int i;
                             5.
                             6.     for (i=0; i<10000; i++)
                                <function: ifunc>
   0.        0.                 [ 6]    10684:  clr         %o0
   0.        0.                 [ 6]    10688:  sethi       %hi(0x2400), %o5
   0.        0.                 [ 6]    1068c:  inc         784, %o5
                             7.         i++;
   0.        0.                 [ 7]    10690:  inc         2, %o0
## 1.440     1.440              [ 7]    10694:  cmp         %o0, %o5
   0.        0.                 [ 7]    10698:  bl,a        0x10694
   0.        0.                 [ 7]    1069c:  inc         2, %o0
   0.        0.                 [ 7]    106a0:  retl
   0.        0.                 [ 7]    106a4:  nop
                             8.     return i;
                             9. }

命令発行遅延

特定のリーフ PC の示す命令の発行前に遅延があると、そのリーフ PC の出現回数が多くなることがあります。これは、次のケースを初めとして、さまざまな多くの理由で発生する可能性があります。

ハードウェアカウンタオーバーフローの関連付け

UltraSPARC プラットフォームでの TLB ミスを除き、オーバーフローにより生成される割り込みの処理に要する時間などのいくつかの理由から、ハードウェアカウンタのオーバーフローイベントの呼び出しスタックは、オーバーフローが発生した時点ではなく命令シーケンスの後ろの方で記録されます。 サイクルおよび発行された命令などの一部のカウンタの場合、この遅延は問題になりません。しかし、キャッシュミスや浮動小数点演算をカウントするようなカウンタの場合は、そのオーバーフローの原因となっているものとは別の命令がメトリックの原因とされます。多くの場合、イベントを発生させた PC は、記録されている PC の数命令前のもので、逆アセンブリリストでその命令を正確に特定できます。ただし、この命令範囲内に分岐先がある場合、イベントを発生させた PC に対 応する命令を見分けることは難しいか、または不可能です。メモリーアクセスイベントをカウントするハードウェアカウンタについて、コレクタはカウンタ名の前に「+」が付いている場合、イベントを発生させた PC を検索します。この方法で記録されたデータは、データ空間プロファイリングをサポートします。詳細は、「データ空間プロファイリング」および -h counter_definition_1...[, counter_definition_n]を参照してください。

「ソース」タブ、「逆アセンブリ」タブ、「PC」タブの特別な行

アウトライン関数

フィードバック最適化コンパイルで、アウトライン関数が作成されることがあります。これらは、「ソース」タブと「逆アセンブリ」タブで、特別なインデックス行として表示されます。「ソース」タブでは、注釈は、アウトライン関数に変換されたコードブロックに表示されます。


                   Function binsearchmod inlined from source file ptralias2.c into the 
0.       0 .         58.         if( binsearchmod( asize, &element ) ) {
0.240    0.240       59.             if( key != (element << 1) ) {
0.       0.          60.                 error |= BINSEARCHMODPOSTESTFAILED;
                        <Function: main -- outline code from line 60 [_$o1B60.main]>
0.040    0.040     [ 61]                 break;
0.       0.          62.             }
0.       0.          63.         }

「逆アセンブリ」タブでは、アウトライン関数は通常、ファイルの末尾に表示されます。


                        <Function: main -- outline code from line 85 [_$o1D85.main]>
0.       0.             [ 85] 100001034:  sethi       %hi(0x100000), %i5
0.       0.             [ 86] 100001038:  bset        4, %i3
0.       0.             [ 85] 10000103c:  or          %i5, 1, %l7
0.       0.             [ 85] 100001040:  sllx        %l7, 12, %l5
0.       0.             [ 85] 100001044:  call        printf ! 0x100101300
0.       0.             [ 85] 100001048:  add         %l5, 336, %o0
0.       0.             [ 90] 10000104c:  cmp         %i3, 0
0.       0.             [ 20] 100001050:  ba,a        0x1000010b4
                        <Function: main -- outline code from line 46 [_$o1A46.main]>
0.       0.             [ 46] 100001054:  mov         1, %i3
0.       0.             [ 47] 100001058:  ba          0x100001090
0.       0.             [ 56] 10000105c:  clr         [%i2]
                        <Function: main -- outline code from line 60 [_$o1B60.main]>
0.       0.             [ 60] 100001060:  bset        2, %i3
0.       0.             [ 61] 100001064:  ba          0x10000109c
0.       0.             [ 74] 100001068:  mov         1, %o3

アウトライン関数の名前は角括弧内に表示され、コードの取り出し元関数の名前や、ソースコード内のセクションの先頭の行番号を含む、アウトライン化したコードのセクションに関する情報をエンコードします。これらの符号化された名前は、リリースごとに異なります。アナライザは、読みやすい関数名を表示します。詳細は、「アウトライン関数」を参照してください。

アプリケーションのパフォーマンスデータの収集中にアウトライン関数が呼び出されると、アナライザは注釈付き逆アセンブリに特別な行を表示して、その関数の包括的メトリックスを示します。詳細は、「包括的メトリックス」を参照してください。

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

関数内のループ、または並列化指令のある領域を並列化する場合、コンパイラは、元のソースコードに含まれていない新しい本体関数を作成します。こうした関数については、「OpenMP ソフトウェア実行の概要」で説明しています。

コンパイラは、並列構造の種類、構造の取り出し元関数の名前、元のソースにおける構造の先頭の行番号、並列構造のシーケンス番号などをエンコードする本体関数に、符号化名を割り当てます。これらの符号化された名前は、マイクロタスクライブラリのリリースごとに異なりますが、より分かりやすい名前に復号化されて表示されます。

次に、関数リストに表示されるような一般的なコンパイラ生成の本体関数を示します。


7.415      14.860      psec_ -- OMP sections from line 9 [_$s1A9.psec_]
3.873       3.903      craydo_ -- MP doall from line 10 [_$d1A10.craydo_]

この例で分かるように、構造が抽出された関数の名前が最初に示され、次に並列構造の種類、並列構造の行番号、コンパイラ生成の本体関数の符号化名が角括弧に表示されます。同様に、逆アセンブリコード内に特別なインデックス行が生成されます。


0.       0.            <Function: psec_ -- OMP sections from line 9 [_$s1A9.psec_]>
0.       7.445         [24]    1d8cc:  save        %sp, -168, %sp
0.       0.            [24]    1d8d0:  ld          [%i0], %g1
0.       0.            [24]    1d8d4:  tst         %i1

0.       0.            <Function: craydo_ -- MP doall from line 10 [_$d1A10.craydo_]>
0.       0.030         [ ?]    197e8:  save        %sp, -128, %sp
0.       0.            [ ?]    197ec:  ld          [%i0 + 20], %i5
0.       0.            [ ?]    197f0:  st          %i1, [%sp + 112]
0.       0.            [ ?]    197f4:  ld          [%i5], %i3

Cray の指令では、関数はソースコード行番号に関連付けされていない可能性があります。このような場合、行番号の代わりに [ ?] が表示されます。注釈付きソースコードにインデックス行が表示される場合は、次のようにインデックス行は行番号なしで命令を示します。


                     9. c$mic  doall shared(a,b,c,n) private(i,j,k)
                  
                   Loop below fused with loop on line 23
                   Loop below not parallelized because autoparallelization 
                     is not enabled
                   Loop below autoparallelized
                   Loop below interchanged with loop on line 12
                   Loop below interchanged with loop on line 12
3.873     3.903         <Function: craydo_ -- MP doall from line 10 [_$d1A10.craydo_],
                      instructions without line numbers>
0.        3.903     10.            do i = 2, n-1

注 –

インデックス行やコンパイラのコメント行は、実際の表示では折り返されません。


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

動的にコンパイルされる関数は、プログラムの実行中にコンパイルされリンクされる関数です。コレクタ API 関数collector_func_load() を使用して必要な情報をユーザーが提供しないかぎり、コレクタは C や C++ で記述された動的にコンパイルされる関数に関する情報を持っていません。「関数」タブ、「ソース」タブ、「逆アセンブリ」タブに表示される情報は、次のように、collector_func_load() に渡される情報によって異なります。

コレクタ API 関数の詳細については、「動的な関数とモジュール」を参照してください。

Java プログラムでは、ほとんどのメソッドが JVM ソフトウェアによってインタプリタされます。別個のスレッドで動作する Java HotSpot 仮想マシンは、インタプリタの実行中にパフォーマンスを監視します。監視プロセス中仮想マシンは、1 つ以上のインタプリタを行なっているメソッドを取り出し、それらのメソッド用のマシンコードを生成し、元のマシンコードをインタプリタするのではなくさらに効率の良いマシンコードバージョンを実行することを決定する場合があります。

Java プログラムでは、コレクタ API 関数を使用する必要はなく、次の例に示すように、アナライザがメソッドのインデックス行の下にある特別な行を使用して、注釈付き逆アセンブリリストに Java HotSpot でコンパイルされたコードが存在することを認識します。


                     11.    public int add_int () {
                     12.       int       x = 0;
2.832     2.832          <Function: Routine.add_int: HotSpot-compiled leaf instructions>
0.        0.             [ 12] 00000000: iconst_0
0.        0.             [ 12] 00000001: istore_1

逆アセンブリリストには、コンパイルされた命令ではなく、インタプリタされたバイトコードのみが示されます。デフォルトでは、コンパイルされたコードのメトリックスは、特別な行の隣りに表示されます。排他的および包括的 CPU 時間は、インタプリタされたバイトコードの各行に示されているすべての包括的および排他的 CPU 時間の合計とは異なります。通常は、何回かメソッドが呼び出されると、コンパイルされた命令の CPU 時間は、インタプリタされたバイトコードの CPU 時間の合計より多くなります。なぜなら、インタプリタされたコードは、メソッドが最初に呼び出されたときに一度だけ実行されるのに対し、コンパイルされたコードはその後も実行されるからです。

注釈付きソースには、Java HotSpot でコンパイルされた関数は表示されません。その代わりに、行番号なしで命令を示す特別なインデックス行が表示されます。たとえば、前述の逆アセンブリの抜粋に対応する注釈付きソースは、次のようになります。


                     11.    public int add_int () {
2.832     2.832        <Function: Routine.add_int(), instructions without line numbers>
0.        0.         12.       int       x = 0;
                       <Function: Routine.add_int()>

Java ネイティブ関数

ネイティブコードは、元は C、C++、または Fortran で記述されたコンパイル済みのコードで、Java コードから Java Native Interface (JNI) を介して呼び出されます。次の例は、デモプログラム jsynprog に関連付けられたファイル jsynprog.java の注釈付き逆アセンブリからの抜粋です。


                     5. class jsynprog
                        <Function: jsynprog.<init>()>
0.       5.504          jsynprog.JavaCC() <Java native method>
0.       1.431          jsynprog.JavaCJava(int) <Java native method>
0.       5.684          jsynprog.JavaJavaC(int) <Java native method>
0.       0.             [  5] 00000000: aload_0
0.       0.             [  5] 00000001: invokespecial <init>()
0.       0.             [  5] 00000004: return

ネイティブメソッドは Java ソースに含まれていないため、jsynprog.java の注釈付きソースの先頭には、行番号なしで命令を示す特別なインデックス行を使って各 Java ネイティブメソッドが表示されます。


0.       5.504          <Function: jsynprog.JavaCC(), instructions without line 
                           numbers>
0.       1.431          <Function: jsynprog.JavaCJava(int), instructions without line 
                           numbers>
0.       5.684          <Function: jsynprog.JavaJavaC(int), instructions without line 
                           numbers>

注 –

実際の注釈付きソースの表示では、インデックス行は折り返されません。


クローン生成関数

コンパイラは、通常以上の最適化が可能な関数への呼び出しを見分けることができます。このような呼び出しの一例として、渡される引数の一部が定数である関数への呼び出しがあります。コンパイラは、最適化できる特定の呼び出しを見つけると、クローンと呼ばれるこの関数のコピーを作成して、最適化コードを生成します。

注釈付きソースでは、コンパイラのコメントは、クローン生成関数が作成されたかどうかを示します。


0.       0.       Function foo from source file clone.c cloned, 
                   creating cloned function _$c1A.foo; 
                   constant parameters propagated to clone
0.       0.570     27.    foo(100, 50, a, a+50, b);

注 –

実際の注釈付きソースの表示では、コンパイラのコメント行は折り返されません。


クローン関数名は、特定の呼び出しを識別する、符号化された名前です。前述の例では、コンパイラのコメントは、クローン生成関数の名前が _$c1A.foo であることを示しています。次に示すように、この関数は関数リストに表示されます。


0.350     0.550     foo
0.340     0.570     _$c1A.foo

逆アセンブリリストクローン生成関数はそれぞれ別の命令のセットを持っているため、注釈付きにはクローン生成関数が別々に表示されます。これらはソースファイルに関連付けられていないため、命令はいずれのソース行番号とも関連付けられていません。次に、クローン生成関数の注釈付き逆アセンブリの最初の数行を示します。


0.       0.           <Function: _$c1A.foo>
0.       0.           [?]    10e98:  save        %sp, -120, %sp
0.       0.           [?]    10e9c:  sethi       %hi(0x10c00), %i4
0.       0.           [?]    10ea0:  mov         100, %i3
0.       0.           [?]    10ea4:  st          %i3, [%i0]
0.       0.           [?]    10ea8:  ldd         [%i4 + 640], %f8

静的関数

静的関数は、ライブラリ内でよく使用されます。これは、ライブラリ内部で使用される関数名が、ユーザーが使用する可能性のある関数名と衝突しないようにするためです。ライブラリをストリップすると、静的関数の名前はシンボルテーブルから削除されます。このような場合、アナライザは、ストリップ済み静的関数を含むライブラリ内のすべてのテキスト領域ごとに擬似的な名前を生成します。この名前は <static>@0x12345 という形式で、@ 記号に続く文字列は、ライブラリ内のテキスト領域のオフセット位置を表します。アナライザは、連続する複数のストリップ済み静的関数と単一のストリップ済み静的関数を区別できないため、複数のストリップ済み静的関数のメトリックスがまとめて表示されることがあります。静的関数の例は、次に示す jsynprog デモの関数リストで参照できます。


0.       0.       <static>@0x18780
0.       0.       <static>@0x20cc
0.       0.       <static>@0xc9f0
0.       0.       <static>@0xd1d8
0.       0.       <static>@0xe204

「PC」タブでは、前述の関数は、次のようにオフセットとともに示されます。


0.       0.       <static>@0x18780 + 0x00000818
0.       0.       <static>@0x20cc + 0x0000032C
0.       0.       <static>@0xc9f0 + 0x00000060
0.       0.       <static>@0xd1d8 + 0x00000040
0.       0.       <static>@0xe204 + 0x00000170

ストリップ済みライブラリ内で呼び出された関数は、「PC」タブで次のように表示される場合もあります。


<library.so> -- no functions found + 0x0000F870

包括的メトリックス

注釈付き逆アセンブリでは、アウトライン関数が要した時間にタグを付けるための特別な行が存在します。

次に、アウトライン関数が呼び出されたときに表示される注釈付き逆アセンブリの例を示します。


0.       0.        43.         else
0.       0.        44.         {
0.       0.        45.                 printf("else reached\n");
0.       2.522         <inclusive metrics for outlined functions>

分岐先

注釈付き逆アセンブリリストに表示される疑似行の <branch target> (分岐先) は、有効アドレスを検索するためのバックトラッキングにおいて、バックトラッキングのアルゴリズムが分岐先に対して実行されたために失敗した命令の PC に対応します。

ストア命令とロード命令の注釈

-xhwcprof オプションでコンパイルすると、コンパイラは、ストア (st) 命令とロード (ld) 命令についての追加情報を生成します。逆アセンブリリストに、注釈付きの st 命令と ld 命令が表示されます。

実験なしのソース/逆アセンブリの表示

実験を実行しなくても、er_src ユーティリティーを使用して、注釈付きソースコードや注釈付き逆アセンブリコードを表示できます。メトリックスが表示されないことを除けば、この表示は、アナライザで生成されるものと同じです。er_src コマンドの構文は次のとおりです。


er_src [ -func | -{source,src} item tag | -{disasm,dis} item tag |
-{cc,scc,dcc} com_spec | -outfile filename | -V ] object

object は、実行可能ファイル、共有オブジェクト、またはオブジェクトファイル (.o ファイル) の名前です。

item は、関数名、または実行可能オブジェクトや共有オブジェクトの構築に使用されたソースまたはオブジェクトファイルの名前です。item は、function'file' の形式でも指定できます。この場合、er_src は指定されたファイルのソースコンテキストに、指定された関数のソースまたは逆アセンブリを表示します。

tag は、同じ名前の関数が複数存在する場合に、参照する item を決定するために使用されるインデックスです。これは必須ですが、関数の解決に不要な場合は無視されます。

特別な item および tag の all -1 は、オブジェクトのすべての関数に対して、注釈付きソースまたは逆アセンブリを生成するように er_src に指示します。


注 –

実行可能ファイルや共有オブジェクトに all -1 を使用した結果生成される出力は、非常に大きくなることもあります。


次に、er_src ユーティリティーに使用可能なオプションについて説明します。

-func

指定オブジェクトのすべての関数を一覧表示します。

-{source,src} item tag

リストされた item の注釈付きソースを示します。

-{disasm,dis} item tag

リストに逆アセンブリを含めます。デフォルトでは、リストに逆アセンブリは含まれません。ソースがない場合は、コンパイラのコメントなしで逆アセンブリのリストが生成されます。

-{cc,scc,dcc} com-spec

表示するコンパイラのコメントクラスを指定します。com-spec は、コロンで区切られたクラスのリストです。com-spec は、-scc オプションが指定されている場合はソースのコンパイラのコメントに、-dcc オプションが指定されている場合は逆アセンブリのコメントに、-cc が指定されている場合は両方のコメントに適用されます。これらのクラスについては、「ソースリストと逆アセンブリリストを管理するコマンド」を参照してください。

コメントクラスは、デフォルト値ファイルで指定することができます。最初にシステム全体の er.rc デフォルト値ファイルが読み取られ、次にユーザーのホームディレクトリの .er.rc ファイルが存在する場合読み取られます。そして現在のディレクトリの .er.rc ファイルが読み取られます。ホームディレクトリの .er.rc ファイル内のデフォルト値はシステムのデフォルト値よりも優先され、現在のディレクトリの .er.rc ファイル内のデフォルト値は、ユーザーのホームおよびシステムのデフォルト値よりも優先されます。これらのファイルは、アナライザと er_print ユーティリティーによっても使用されますが、er_src ユーティリティーが使用するのは、ソースおよび逆アセンブリのコンパイラのコメントに関する設定の部分だけです。デフォルト値ファイルの詳細は、「デフォルト値を設定するコマンド」を参照してください。er_src ユーティリティーは、デフォルト値ファイル内の scc および dcc 以外のコマンドを無視します。

-outfile filename

リストの出力用ファイル filename を開きます。デフォルトの場合、またはファイル名がダッシュ (-) の場合は、出力は stdout に書き込まれます。

-V

現在のリリースバージョンを表示します。