実験の注釈付きソースコードは、パフォーマンスアナライザで「アナライザ」ウィンドウの左の区画にある「ソース」タブを選択することで表示できます。または、実験を実行しなくても、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 つの場所にまとめることによってパフォーマンスを向上させることができます。たとえば、コードブロックの if と else の分岐の両方で同じ演算が記述されている場合、コンパイラはその演算を 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-prefix と new-prefix を指定できます。ソースファイル、オブジェクトファイル、または共有オブジェクトのパス名が old-prefix で指定した接頭辞で始まる場合、古い接頭辞は new-prefix で指定した接頭辞に置き換えられます。結果のパスは、ファイルの検索に使用されます。複数の pathmap コマンドが提供されており、それぞれが、ファイルが見つかるまで試行されます。
コンパイル処理では、要求される最適化レベルに応じて多くの段階があり、変換によって命令とソース行のマッピングに混乱が生じることがあります。最適化によっては、ソース行の情報が完全に失われたり、混乱が生じたりすることがあります。コンパイラは、さまざまな発見手法によって命令のソース行を追跡しますが、こうした手法は絶対ではありません。
命令のメトリックスについては、実行対象の命令を待っている間に発生したメトリックスとして解釈する必要があります。イベントが記録されるときに実行中である命令がリーフ PC と同じソース行に存在している場合、メトリックスはこのソース行を実行した結果であると解釈できます。ただし、実行中の命令とリーフ PC が存在しているソース行がそれぞれ異なる場合、リーフ PC が存在しているソース行のメトリックスの少なくとも一部は、実行中命令のソース行が実行待ちしていた間に集計されたメトリックスであると解釈する必要があります。この一例としては、1 つのソース行で計算された値が次のソース行で使用される場合が挙げられます。
メトリックスの解釈方法がもっとも問題となるのは、キャッシュミスやリソース待ち行列ストールなど、実行が大幅に遅延している場合や、命令が直前の命令の結果を待っている場合です。このような場合、ソース行のメトリックスが異常に高く見えることがあります。コード内のほかのソース行を調べて、こういった高メトリック値の原因である行を突きとめてください。
表 7–1 に、注釈付きソースコードの行に表示可能な 4 種類のメトリックスを示します。
表 7–1 注釈付きソースコードのメトリックス