| プログラムのパフォーマンス解析 |
第 7 章
ループ解析ツール
Fortran および Cコンパイラは、並列化しても安全が損なわれることなく、かつ並列化することが効果的であると判断されるループを対象として、自動的に並列化を行います。パフォーマンス解析ツールであるループツールは、これらのコンパイラによって作成されるループタイミングファイルを読み取ります。ループツールにはグラフィカルユーザインタフェース (GUI) が用意されています。また、ループツールのコマンド行バージョンとしてループレポートも利用できます。
基本概念
ループツールはループルーチンのグラフを表示し、どのループが並列化されたかを示します。ループのグラフ表示画面から任意のループのソースコードへと直接移動することにより、ループツールの中でソースコードを直接編集できます。
ループレポートは、グラフィカルな表示方法ではなく、ASCII ファイルとして、ループ実行時のレポートを作成します。
ループツールおよびループレポートを使用するための基本的な 4 つの手順を示します。
- 環境変数を設定する。
- ループ解析用のタイミングファイルを作成するためのオプションを使って、プログラムをコンパイルする。
- プログラムを実行して、タイミングファイルを作成する。
- そのタイミングファイル上でループツールまたはループレポートを起動する。
注 - この項の例は Fortran (f77 と f95) コンパイラを使用していますが、表示されているオプション (-xparallel、-Zlp など) は、C コンパイラでも利用できます。
環境の設定
-Zlp オプションをつけて実行可能ファイルをコンパイルする前に、使用するマシンに搭載されたプロセッサの数を環境変数 PARALLEL に設定します。以下のコマンドでは、psrinfo というシステムユーティリティを使用しています。逆引用符の内部に注目してください。
%setenv PARALLEL `/usr/sbin/psrinfo | wc -l`このコマンドはシェル起動ファイル (.cshrc または .profile) に入れることができます。
ループタイミングファイルの生成
ループタイミングファイルを生成するには、自動的にコードを並列化し、最適化するコンパイラオプション (-xparallel と -xO4) を使って、プログラムをコンパイルします。 また、ループツールやループレポート用にコンパイルを行うには、
-Zlp オプションを追加します。これらのオプションを使ってプログラムをコンパイルすると、Sun WorkShop はループツールおよびループレポートが処理するためのタイミングファイルを生成します。これら 3 つのコンパイルオプションの使用例を以下に示します。
% f77 -xO4 -xparallel -Zlp source_file
注 - いずれの例も FORTRAN 77、Fortran 95、および C プログラムに適用されます。
ループの確認および並列化に使用できる便利なオプションがほかにも多数あります。表 7-1に、これらのオプションを示します。
表 7-1 コンパイルオプション -oprogram実行可能ファイル名を program に変更します。 -xexplicitparDOALL プラグマの付いたループを並列化します。 -xloopinfostderr にヒントを出力します。
その他のコンパイラオプション
ループツールおよびループレポートでは、各種のコンパイラオプションを組み合わせることができます。
自動並列化用にコンパイルする場合は、通常は
-xparallelおよび-x04というコンパイラオプションを指定します。ループツールとループレポート用のコンパイルを行うには、-Zlpを追加します。
%f77 -x04 -xparallel -Zlpsource_file
-xO3 または -xO4 と -xparallel は併用できます。-xO3、-xO4 のいずれも指定せず、-xparallel のみ指定する場合、コンパイラは -xO3 を使用します。表 7-2 には、特定のオプションについての最適化レベルオプションの追加方法をまとめてあります。
表 7-2 最適化レベルオプションと暗黙指定 -xparallel-xparallel -xO3-xparallel -Zlp-xparallel -xO3 -Zlp-xexplicitpar-xexplicitpar -xO3-xexplicitpar -Zlp-xexplicitpar -xO3 -Zlp-Zlp-xdepend -xO3 -Zlp
これ以外に、-xexplicitpar と -xloopinfo というコンパイルオプションがあります。
Fortranコンパイラオプション -xexplicitpar は、 プラグマ DOALL とともに使用されます。ソースコード中で、あるループの前に DOALL を挿入すると、並列化のための明示的なマークが付けられます。-xexplicitpar を指定してコンパイルすると、そのループが並列化されます。
以下のコードは、ループの並列化を明示的にマークする方法を示したものです。
subroutine adj(a,b,c,x,n)real*8 a(n), b(n), c(-n:0), xinteger nc$par DOALLdo 19 i = 1, n*ndo 29 k = i, n*na(i) = a(i) + x*b(k)*c(i-k)29 continue19 continuereturnend-Zlp を単独で使用すると、-xdepend と -xO3 が追加されます。-xdepend オプションは、コンパイラに対してループを識別するときに必要なデータ依存関係の解析を実行するよう指示します。-xparallel オプションには -xdepend も含まれますが、-xdepend は -xparallel の暗黙指定(トリガー)ではありません。
-xloopinfo オプションは、プログラムのコンパイル時に、ループに関するヒントを stderr (ファイル記述子 2 の UNIX 標準エラーファイル)に出力します。このヒントには、ルーチン名、ループの開始地点を示す行番号、ループの並列化の有無、並列化されていない場合はその理由などが示されます。
以下の例では、ソースファイル gamteb.F のループに関するヒントをファイル gamtab.loopinfo にリダイレクトしています。
% f77 -xO3 -parallel -xloopinfo -Zlp gamteb.F 2> gamteb.loopinfo-Zlp と -xloopinfo の最大の違いは、ループに関するコンパイラヒントを出力することに加えて、-Zlpによって実行時にタイミングに関する統計情報を記録するようになるという点です。このため、ループツールとループレポートは、-Zlp を指定してコンパイルされたプログラムだけを対象として解析を行います。
プログラムの実行
-Zlpを指定してコンパイルした後、実行可能ファイルを実行してください。この結果、ループタイミングファイル program.looptimes が作成されます。ループツールとループレポートは、計測機構付きの実行可能ファイルとループタイミングファイルの 2 つのファイルを処理します。
ループツールの起動
読み込むプログラム (実行可能ファイル) の名前を指定することにより、ループツールを起動できます。
%looptoolprogram&ファイル指定なしにループツールを起動した場合、「ファイルを開く」ダイアログボックスが開き、検証するファイルの選択を要求されます。
%looptool &ループツールは、プログラムに関連するタイミングファイルを読み込みます。タイミングファイルには、ループに関する情報が記載されています。通常、タイミングファイルには program.looptimes という形式の名前が付けられ、プログラムと同じディレクトリに入れられます。
デフォルトでは、ループツールは実行可能ファイルのあるディレクトリを検索して、タイミングファイルを探します。したがって、タイミングファイルが所定のディレクトリにあれば (正常な状態) 、探す場所を指定する必要はありません。
%looptoolprogram&コマンド行でタイミングファイルの名前を指定すると、ループツールおよびループレポートはそのファイルを使用します。
%looptoolprogram program.looptimes &コマンド行オプション -p を指定すると、ループツールおよびループレポートは -p で示されたディレクトリを検索して、タイミングファイルを探します。
%looptool -ptiming_file_directoryprogram &環境変数
LVPATH が設定されている場合、ツールはLVPATHで指定されたディレクトリを検索して、タイミングファイルを探します。
%setenv LVPATHtiming_file_directory%looptoolprogram &ループツールの使用
メインウィンドウに、ソースファイルがコンパイラに渡された順序に従って、プログラムにある各ループの各実行時間が棒グラフとして表示されます。
図 7-1には、ループツールウィンドウの構成要素を示します。
![]()
図 7-1 「ループツール」ウィンドウファイルを開く
実行可能ファイルとタイミングファイルを開くには、メインウィンドウの「ファイル」メニューの「開く」コマンドを選択します。
実行可能ファイルのパスを入力した後は、タイミングファイルを指定する必要はありません。ただし、タイミングファイルが別のディレクトリにある場合やデフォルト以外の名前を使用している場合は除きます。
ファイルを開く方法の詳細については、Sun WorkShop オンラインヘルプの「プログラム中のループの分析」を参照してください。
全ループに関するレポートの作成
プログラム中にある全ループに関する情報を含むウィンドウ (図 7-2 参照) を開くには以下を行います。
メインウィンドウの「ファイル」メニューから「レポートを作成」を選択します。 (図 7-2 参照)
ここで作成されるレポートは、ループレポートで作成されるものと同じです。
レポートウィンドウの「ヘルプ」 ボタンは、コンパイラに関するヒントを含む Sun WorkShop オンラインヘルプの関連項目にリンクされています。
![]()
図 7-2 ループレポートループツールグラフの印刷
ループツールのグラフを印刷するには、メインウィンドウの「ファイル」メニューから 「グラフを印刷」を選択し、選択しているプリンタ名を入力します。グラフをファイルに保存するには、プリンタ名の代わりにファイル名を入力します。
印刷についての詳細は、Sun WorkShop オンラインヘルプを参照してください。
エディタの選択
エディタを選択するには、メインウィンドウの「ファイル」メニューから「オプション」コマンドを選択し、「テキストエディタのオプション」ダイアログボックスでソースコードの編集に使用するエディタを選択します。vi、mule または xemacs のエディタが使用可能です。
注 - vi と xemacs は、ループツールと同じディレクトリ(通常は /opt/SUNWspro/bin) にインストールされます (エディタがまだシステムにインストールされていない場合に限ります) 。mule の場合は、ユーザー自身がインストールする必要があります。いずれの場合も、使用するエディタは、ループツールが検出できるように検索パス上のディレクトリに存在しなければなりません。たとえば、mule をシステム上の /usr/local に置く場合、PATH 環境変数にはこのパスが指定されていなければなりません。
エディタの選択の詳細は、Sun WorkShop のオンラインヘルプの「テキストエディタの選択」を参照してください。
ソースコードの編集とヒント
メインウィンドウ上 (図 7-1) でループを選択すると、以下の 2 つの動作が行われます。
- ソースコードを編集するためのウィンドウが表示され (
図 7-3参照) 、vi、xemacs、muleの各 エディタが使用可能となります。
- vi の詳細については、vi(1) のマニュアルページを参照してください。また xemacs および mule については、オンラインヘルプが用意されています (「ヘルプ」 ボタンをクリックしてください) 。
- Sun WorkShop の vi エディタには、「バージョン」という特殊なメニューがあります。このメニューでは、SCCS (ソースコード管理システム) ユーティリティを使用して、ファイルを共有化できます。詳細については、ループツールのオンラインヘルプおよび sccs(1) のマニュアルページを参照してください。
- 選択したループに関する 1 つあるいは複数のヒントを含む「ヒント」ウィンドウが表示されます。このウィンドウの「ヘルプ」ボタンを押すと、WorkShop オンラインヘルプのコンパイラヒントに関連する内容が表示されます。ヒントの詳細については、「コンパイラヒント」を参照してください。
図 7-3 には、ループが選択された状態の xemacs エディタウィンドウと、コンパイラヒントの説明が表示されたヒントウィンドウを示します。
![]()
図 7-3 テキストエディタとヒントウィンドウ
注意 - ソースコードを編集すると、ループツールで表示される行番号がソースの行番号と異なる場合があります。編集済みのソースをいったん保存し、これを再コンパイルしてから最新の実行可能ファイルとともにループツールを起動すると、新しいループ情報が表示され、行番号が一致するようになります。
ループレポートの起動
ループレポートの起動時に、プログラム名を入力します。loopreport の後ろに検証したいプログラム名 (実行可能ファイル) を入力してください。
%loopreportprogramプログラム名を指定せずにループレポートを起動することも可能です。ただし、プログラム名を指定せずにループレポートを起動する場合、ループレポートは現在の作業ディレクトリを検索して、a.out という名前のファイルを探します。
% loopreport > a.out.loopreport出力をファイルにリダイレクトしたり、パイプによってほかのプログラムへと受け渡すことも可能です。
%loopreportprogram>program.loopreport%loopreportprogram| moreタイミングファイル
ループレポートはプログラムに関連したタイミングファイルを一緒に読み込みます。タイミングファイルは、-zlp オプションを使用する場合に作成され、ループに関する情報を含みます。通常、タイミングファイルには program.looptimes という形式の名前が付けられ、プログラムと同じディレクトリに収められます。
しかし、 タイミングファイルの位置を指定する方法も 4 種類ほど用意されています。ループレポートは、以下に示すルールに従ってタイミングファイルを選択します。
- タイミングファイルがコマンド行で指定された場合、ループレポートはそのファイルを使用します。
%loopreportprogram newtimes>program.loopreport- コマンド行オプション -p が使用された場合、ループレポートは -p によって指定されるディレクトリからタイミングファイルを探します。
%loopreportprogram-p/home/timingfiles>program.loopreport環境変数 LVPATH が設定されている場合、ループレポートはLVPATHで指定されるディレクトリからタイミングファイルを探します。
%setenv LVPATH/home/timingfiles%loopreportprogram>program.loopreport- ループレポートはループ統計情報のテーブルを標準出力である stdout に書き込みます。この出力はファイルにリダイレクトしたり、パイプによってほかのコマンドに受け渡すことも可能です。
%loopreportprogram>program.loopreport%loopreportprogram| more図 7-4 に、ループレポートの例を示します。
図 7-4 ループレポートの例ループレポートのフィールド
以下の説明は、ループツールの「レポートの作成」出力およびループレポートの出力にもそのままあてはまります。
- ループ ID
- 行番号
- 並列
- ヒント
- 回数
- 上位からそのループへと入り込む回数。これは、ループが実行される総数であるループ反復回数とは異なります。たとえば、以下のコードは Fortran で書かれた 2 つのループです。
do 10 i=1,17do 10 j=1,50...some code...10 continue- 最初のループには 1 度だけ入り、ループは 17 回反復されます。2 番目のループには 17 回入り込み、ループは 17*50 = 850 回反復されます。
- 入れ子
- ループの入れ子のレベル。ループがトップレベルのループならば、入れ子のレベルは 0 です。ループがほかのループの子ループならば、レベルは 1 となります。
- たとえば、以下に示す C のコードでは、
iループはレベル 0、jループはレベル 1、kループはレベル 2 となります。
for (i=0; i<17; i++)for (j=0; j<42; j++)for (k=0; k<1000; k++)do something;- 時計時間
- プログラム全体に対して、このループを実行するために費やされる経過時計時間 (待ち時間) の総計。外側のループの経過時計時間には、内側のループの経過時計時間が含まれます。以下に例を示します。
for (i=1; i<10; i++)for (j=1; j<10; j++)do something;- たとえば、上記の例で、外側のループ (
iループ) に割り当てられる時間が 10 秒だとしたら、内側のループ (jループ) に割り当てられる時間は 9.9 秒となります。- % (割合)
- 変数
上記のループ例が並列処理で実行される場合、a(2) の値を基に b(1) の値を再計算する反復 1 は、a(2) の再計算を終えたばかりの反復 2 の後に実行される可能性が出てきます。つまり、ループが並列処理されなかった場合とは異なり、b(1) の値は、オリジナルの値ではなく、新しい a(2) の値によって決まってしまいます。
コンパイラヒント
ループツールとループレポートは、特定のループに対して適用された最適化についてのヒント、そして、なぜ並列化が行われなかったかについてのヒントを提示します。最適化の間にコンパイラが生成するヒントは、わかりにくい場合もあります。こうしたヒントは全体の文脈の中で理解されるべきものであり、特定のループに対して生成されるコードについての絶対的な事実ではありません。ただし、コンパイラがループの並列化を含めたより一層の最適化を実行できるように、コードを変換する方法についての重要な指針となることも少なくありません。
並列化の上で参考になる説明とヒントについては、『 Fortran ユーザーズガイド』を参照してください。
0. ヒントはありません。
このループに適用されるヒントはありません。これは、その他のヒントがまったく適用されないという意味ではなく、単にコンパイラから導かれるヒントがないということです。
1. ループ中に手続き呼び出しがあります。
ループ中にマルチスレッドで動作すると安全でない手続き呼び出しが含まれているので、並列化できません。このようなループを並列化すると、ループの複数のコピーによって関数呼び出しが同時にインスタンス化され、この関数に固有の変数や戻り値に悪影響を与えたり、関数の目的を無効にする可能性があります。このループ中の手続き呼び出しがマルチスレッドで使用しても安全なことがわかっている場合は、ループ本体の前に
DOALLプラグマを追加することにより、このループを無条件に並列化するよう指示できます。たとえば、fooがマルチスレッドで使用しても安全な関数呼び出しである場合、c$par DOALLを挿入することによって、強制的な並列化を指定できます。
c$par DOALLdo 19 i = 1, n*ndo 29 k = i, n*na(i) = a(i) + x*b(k)*c(i-k)call foo()29 continue19 continueコンパイラは
-parallelまたは-explicitparを指定してコンパイルした場合に限り、DOALLプラグマを解釈します。-autoparを指定してコンパイルすると、DOALLプラグマは無視されます。2. コンパイラはこのループに対して 2 つのバージョンを生成しました。
コンパイル時には、ループの中に並列化する意味のある要素が含まれているかどうか、コンパイラは判断できません。コンパイラは、逐次形式と並列形式の 2 つのバージョンのループを生成します。実行時の検査によって、どちらのバージョンを使用するかを決定します。この実行時検査は、ループの繰り返し値を検査して、ループの処理量を判断します。
3. 変数 list によってこのループ内でデータ依存性が発生する
ループ中の変数が、前回の繰り返しに使用された変数の値による影響を受けます。たとえば、次のとおりです。
do 99 i=1,ndo 99 j = 1,ma[i, j+1] = a[i,j] + a[i,j-1]99 continueこれは、あくまで説明の便宜上、意図的に考えた例にすぎません。このような単純なループの場合は、単にオプティマイザによって、内側と外側のループを入れ替えて、内側のループを並列化できますが、この例は、「ループ伝播データ依存性」と呼ばれるデータの依存関係の概念をわかりやすく示したものでもあります。
コンパイラは、ループ伝播データ依存性の原因となっている変数の名前を特定できる場合があります。プログラムを変更して、この種の依存関係を取り除く (または最小限に抑える) ことにより、コンパイラはさらに積極的な最適化を実行できるようになります。
4. 最適化の間に、ループが大幅に変形されています。
コンパイラがこのループを最適化した結果、生成されたコードとソースコードの対応が取れなくなる場合があります。このため、行番号の整合性がなくなっている可能性があります。ループを大幅に変更する可能性のある最適化の例として、ループの分散化、融合、交換 (·"·鵐� <Default 示 Font>10、·"·鵐� <Default 示 Font>11、および ·"·鵐� <Default 示 Font>12 を参照) などがあります。
5. ループは、並列化動作によって効果が得られるかどうか不明です。
コンパイラは、ループが並列化動作によって効果が得られるかどうかを、コンパイル時には判断できません。このヒントが表示されたループは、場合によって、「並列」のラベルが付けられることがあります。これは、コンパイラが 2 つのバージョンのループを生成 (·"·鵐� <Default 示 Font>2を参照) し、並列バージョンと逐次バージョンのいずれを使用するかは実行時に決定するという意味です。
コンパイラヒントは、ループの並列化の有無を示すフラグも含め、すべてコンパイル時に生成されるので、「並列」とラベル付けされたループが実際に並列的に実行されるかどうかについて確証はありません。
6. ループにユーザーが挿入した
DOALLプラグマが付いています。このループは、コンパイラに対する
DOALLプラグマの指示によって並列化されています。このヒントは、明示的に並列化を指定したループを簡単に識別する場合に利用できます。
DOALLプラグマは、-parallelまたは-explicitparを指定してコンパイルする場合に限り、コンパイラにより解釈されます。-autoparを指定してコンパイルすると、コンパイラはDOALLプラグマをすべて無視します。7. ループ中に複数の出口があります。
ループの中に、通常のループの出口以外に、GOTO やループを終了するその他の分岐が使用されています。したがって、コンパイラではループの実行時の動作が予測できないため、ループの並列化は危険と判断されます。
8. ループ中にマルチスレッドで安全でない I/O または他の関数呼び出しがあります。
これは、ヒント 1 に似ています。ただし、ヒント 8 がマルチスレッドで動作すると安全でない「入出力」に適用され、ヒント 1 はマルチスレッドで動作すると安全でない「関数の呼び出し」に適用される点が異なります。
9. ループ中に制御が逆方向に進む箇所があります。
ループの中に、
GOTOや、逆方向に流れたりループ本体から抜けたりするような制御フローが使用されています。すなわち、コンパイラはループの内部にある文が、すでに実行済みのコードに戻るフローになっていると解釈します。複数の出口が存在するループの場合と同様に、このループを並列化することは安全ではありません。逆方向の制御フローを取り除く (または最小限に抑える) ことにより、コンパイラはより積極的な最適化を実現できるようになります。
10. ループは分散して実行されている可能性があります。
ループの内容が、何度か繰り返すうちに分散している可能性があります。すなわち、ループを並列化できるようにコンパイラによってループ本体が書き換えられている可能性があります。ただし、この書き換えはオプティマイザの内部表現で使用する言語によって行われるので、もとのソースコードと書き換えたコードを対応させるのはきわめて困難になります。このため、分散されたループについてのヒントでは、実際のソースコードにある行番号には一致しない行番号が示される可能性があります。
11. 複数のループが融合されている可能性があります。
並列化の効果を高めるよう大きなループにするため連続した 2 つのループが 1 つにまとめられています。また、この場合も、ソースの行番号が誤っている可能性があります。
12. 複数のループが交換されている可能性があります。
内部ループと外部ループのインデックスが入れ替えられています。これは、データの依存関係を内部ループから可能な限り遠い位置に置き、この入れ子ループを並列化するための処置として行うものです。深いレベルで入れ子にしたループの場合は、3 つ以上のループで交換が行われている可能性があります。
コンパイラの最適化とループへの影響
コンパイラヒントの説明からもわかるとおり、最適化コードとソースコードを対応させるのは難しい場合があります。コンパイラが出力する情報が、可能な限りソースコードに近い形式で表示されるほうが望ましいのは明らかです。残念ながら、コンパイラオプティマイザは、プログラムを内部表現によって「読み取る」ため、もとのソースコードに対応させようとしても、それほど効果はありません。
インライン化
インライン化とは、 最適化レベル
-O4に限り、1 つのファイルに含まれる関数だけに 適用される最適化の 1 つです。すなわち、あるファイルに 17 個の Fortran 関数が含まれている場合、16 個の関数を残りの 1 つの関数に展開でき、-O4レベルでコンパイルされていれば、その 16 個の関数のソースコードを 1 つの関数の本体にコピーします。これ以上の最適化を適用すると、ソースコードのどの行番号がどの最適化の対象となっているかが特定できなくなります。コンパイラヒントの説明が不明確と思われる場合は、
-O3 -parallel -Zlpを付けてコンパイルすると、コンパイラが関数をインライン化する前の段階で、ループに関するコンパイラからの指摘をより詳しく確認できます。特に実体のないループ (すなわちコンパイラが、実在することを前提とするループで、実際のソースコード中には存在しないもの) は、インライン化の対象となっている可能性があります。
ループの変形 : 展開、詰め込み、分割、および入れ替え
コンパイラは、ループ本体を大きく書き換えるようなループの最適化を多数実行します。 具体的には、ループの最適化、展開、詰め込み、分割、入れ替えなどの操作があります。
ループツールとループレポートは、可能な限り、意味のあるヒントを提供しようとしますが、最適化済みコードとソースコードを対応させる上での根本的な問題があるため、ヒントの内容が誤解を招くこともあります。
逐次ループの内部に入れ子にした並列ループ
逐次ループの中に並列ループが入れ子にされている場合、各ループを繰り返すごとに時計時間を使用するという条件があるため、ループツールおよびループレポートが報告する実行時間の情報が誤解を招く場合があります。内部ループが並列化されていると、ループの一部を並列的に繰り返す場合であっても、反復ごとの時計時間が確保されます。
これに対して、外部ループには、入れ子にした並列ループ全体の実行時間しか割り当てられません。すなわち、内部ループを並列的にインスタンス化した場合の最も長い時間に相当します。このように計測方法が二重になる結果、内部ループに比べ、外部ループの消費時間が短くなるという、「外部ループの変則性」という問題を生じることになります。
|
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |