この記事は、インテル® デベロッパー・ゾーンに掲載されている「Annotated source listing with optimization reports」(https://software.intel.com/en-us/articles/intel-compiler-new-feature-annotate-source-code-lines-with-optimization-reports) の日本語参考訳です。
インテル® C++/Fortran コンパイラー 17.0 以降では、最適化サポートをソースコード行に注釈する新しいオプションが提供されています。
訳者注: Windows* 上での日本語版のインテル® コンパイラーは、日本語のアノテーションを正しく生成できないことがあります。生成されたファイルが文字化けする際は、コンパイルオプションに /Qdiag-message-catalog- を追加して英語メッセージに切り替えてください。インテル® C++/Fortran コンパイラー 19.1 では、この問題が解決されています。
シンタックス
Windows*: /Qopt-report-annotate[:<キーワード>]
Linux* および macOS*: -qopt-report-annotate[=<キーワード>]
キーワードに指定されるフォーマットで最適化レポート付きのソースコードを出力します。
指定可能なフォーマットは以下です。
html – HTML 形式で出力します
text – テキスト形式で出力します (デフォルト)
Windows*: /Qopt-report-annotate-position:<キーワード>
Linux* および macOS*: -qopt-report-annotate-position=<キーワード>
最適化レポートがソースコードのどの位置に挿入されるかを指定します。
指定可能なキーワードは以下です。
caller – 関数の呼び出し側
callee – 呼び出し先の関数
both – caller と callee の両方
スコープと利用例
Windows* コマンドラインのユーザーと Linux* ユーザーは、この機能を使用して最適化レポートとソースリストを統合できます。インライン展開された注釈付きのソースで、ループに関連する最適化レポートが表示される位置を指定します。
- このオプションは、コンパイラーがインライン展開した関数のみに関連し、関数がインライン展開された場所、またはインライン展開された関数自身かその両方が終了する呼び出し先を指定します。
- このオプションは、最適化に関連するループのみを考慮します (例えば、LOOP BEGIN (ループの開始) で始まり、LOOP END (ループの終了) で終わる出力部分)。
例
#include <omp.h>
#include <time.h>
#include <stdio.h>
double f(double x);
static long num_steps = 1000000000;
double step, start, end;
int main ()
{
int i;
double x, pi, sum = 0.0;
step = (double)1.0/(double)num_steps;
start = clock();
// #pragma omp simd reduction(+:sum) private(x)
for (i=0;i< num_steps; i++){
x = (i+0.5)*step;
sum = sum + f(x);
}
pi = step * sum;
end = clock();
printf("Pi = %f Time = %f\n", pi, (double)((end-start)/CLOCKS_PER_SEC));
}
double f(double x){
return 4.0/(1.0 + x * x);
}
上記のソースコードを次のコマンドラインでコンパイルします。
# icl /Qopt-report5 /Qopt-report-annotate /Qopt-report-annotate-position=callee pi.c (Windows*)
# icc -qopt-report5 -qopt-report-annotate -qopt-report-annotate-position=callee pi.c (Linux*)
ワーキング・ディレクトリーに “pi.c.annot” という注釈付きのソースファイルが作成されます。pi.c.annot を開くと、注釈付きのソース行を見ることができます。
出力された pi.c.annot:
//
// ------- Annotated listing with optimization reports for "D:\pi.c" -------
//
//インテル(R) 64 対応インテル(R) C++ コンパイラー (インテル(R) 64 対応アプリケーション用) バージョン 19.1.0.166 ビルド 20191121
//
//コンパイラー・オプション: /Qopt-report5 /Qopt-report-annotate /Qopt-report-annotate-position=callee
// プログラム全体 (SAFE) [いずれかのメソッド]: false
// プログラム全体 (SEEN) [テーブルメソッド]: true
// プログラム全体 (READ) [オブジェクト・リーダー・メソッド]: false
//
//インライン展開オプション値:
// -Qinline-factor: 100
// -Qinline-min-size: 30
// -Qinline-max-size: 230
// -Qinline-max-total-size: 2000
// -Qinline-max-per-routine: 10000
// -Qinline-max-per-compile: 500000
//
//インライン展開レポートの説明:
// "sz" はルーチンの "size" を指します。ルーチンのサイズが小さいほど
// インライン展開の可能性は高くなります。
// "isz" はルーチンの "inlined size" を指します。これは呼び出した
// ルーチンがインライン展開された場合の呼び出しルーチンのサイズです。
// コンパイラーは、一般にルーチンをインライン展開してルーチンのサイズを
// 制限します。
//
1 // use /Ox option
2
3 #include <omp.h>
4 #include <time.h>
5 #include <stdio.h>
6
7 double f(double x);
8 static long num_steps = 1000000000;
9 double step, start, end;
10
11 int main ()
12 {
//インライン展開レポート: (main()) [1/5=20.0%] D:\pi.c(12,1)
// -> EXTERN: (18,9) clock(void)
// -> INLINE: (23,14) f(double) (isz = 2) (sz = 8)
// -> EXTERN: (26,7) clock(void)
// -> (28,1) printf(const char *const, ...) (isz = 20) (sz = 27)
// [[ 呼び出しをインライン展開できません。 <1>]]
//
//D:\pi.c(12,1):リマーク #34051: レジスター割り当て: [main] D:\pi.c:12
//
// ハードウェア・レジスター
// 予約済み : 2[ rsp rip]
// 利用可能 : 39[ rax rdx rcx rbx rbp rsi rdi r8-r15 mm0-mm7 zmm0-zmm15]
// 呼び出し先セーブ : 18[ rbx rbp rsi rdi r12-r15 xmm6-xmm15]
// 割り当て済み : 16[ rax rdx rcx r8 zmm0-zmm5 zmm10-zmm15]
//
// ルーチンの一時変数
// 合計 : 80
// グローバル : 35
// ローカル : 45
// 再生成 : 12
// 退避 : 6
//
// ルーチンスタック
// 変数 : 4 バイト*
// 読み取り : 2 [7.25e-001 ~ 0.7%]
// 書き込み : 2 [7.25e-001 ~ 0.7%]
// 退避 : 96 バイト*
// 読み取り : 6 [2.10e+000 ~ 2.1%]
// 書き込み : 6 [2.10e+000 ~ 2.1%]
//
// 注
//
// *オーバーラップしない変数と退避はスタック空間を共有できるため、
// 合計スタックサイズはこれよりも小さくなる可能性があります。
//
//
13 int i;
14 double x, pi, sum = 0.0;
15
16 step = (double)1.0/(double)num_steps;
17
18 start = clock();
19
20 // #pragma omp simd reduction(+:sum) private(x)
21 for (i=0;i< num_steps; i++){
//
//ループの開始 D:\pi.c(21,6)
// リマーク #15305: ベクトル化のサポート: ベクトル長 2
// リマーク #15399: ベクトル化のサポート: アンロールファクターが 4 に設定されます。
// リマーク #15309: ベクトル化のサポート: 正規化されたベクトル化のオーバーヘッド 0.103
// リマーク #15355: ベクトル化のサポート: sum は double 型のリダクションです。 [ D:\pi.c(14,19) ]
// リマーク #15300: ループがベクトル化されました。
// リマーク #15475: --- ベクトルのコストサマリー開始 ---
// リマーク #15476: スカラーのコスト: 46
// リマーク #15477: ベクトルのコスト: 25.500
// リマーク #15478: スピードアップの期待値: 1.800
// リマーク #15486: 除算: 1
// リマーク #15487: 型変換: 1
// リマーク #15488: --- ベクトルのコストサマリー終了 ---
// リマーク #25015: ループの最大トリップカウントの予測=125000000
//ループの終了
22 x = (i+0.5)*step;
23 sum = sum + f(x);
24 }
25 pi = step * sum;
26 end = clock();
27
28 printf("Pi = %f Time = %f\n", pi, (double)((end-start)/CLOCKS_PER_SEC));
29 }
30
31 double f(double x){
//インライン展開レポート: (f(double)) [5/5=100.0%] D:\pi.c(31,19)
//
//D:\pi.c(31,19):リマーク #34051: レジスター割り当て: [f] D:\pi.c:31
//
// ハードウェア・レジスター
// 予約済み : 2[ rsp rip]
// 利用可能 : 39[ rax rdx rcx rbx rbp rsi rdi r8-r15 mm0-mm7 zmm0-zmm15]
// 呼び出し先セーブ : 18[ rbx rbp rsi rdi r12-r15 xmm6-xmm15]
// 割り当て済み : 2[ zmm0-zmm1]
//
// ルーチンの一時変数
// 合計 : 25
// グローバル : 0
// ローカル : 25
// 再生成 : 1
// 退避 : 0
//
// ルーチンスタック
// 変数 : 0 バイト*
// 読み取り : 0 [0.00e+000 ~ 0.0%]
// 書き込み : 0 [0.00e+000 ~ 0.0%]
// 退避 : 0 バイト*
// 読み取り : 0 [0.00e+000 ~ 0.0%]
// 書き込み : 0 [0.00e+000 ~ 0.0%]
//
// 注
//
// *オーバーラップしない変数と退避はスタック空間を共有できるため、
// 合計スタックサイズはこれよりも小さくなる可能性があります。
//
//
32 return 4.0/(1.0 + x * x);
33 }//インライン展開注釈:
//
//<1> 関数をインライン展開するとプログラムが正しく動作しなくなる可能性があります。
//
//<2> ルーチンの宣言で "__declspec(noinline)" が指定されています。この
// ルーチンのインライン展開を許可するには、これを削除してください。
//
上記のレポートから、インライン展開されたループの最適化レポートが生成されていることが分かります。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

