インテルのみ表示可能 — GUID: wfa1476380079940
Ixiasoft
2.8.5. Single Work-Itemカーネルのループ
新しいループ反復の開始頻度は開始間隔(II)と呼ばれます。 IIは、パイプラインが次のループ反復を処理する前に待機しなければならないハードウェアクロックサイクルの数を示します。最適にアンロールされたループは、1つのループ反復がクロックサイクルごとに処理されるため、IIの値が1です。
HTMLレポートでは、最適に展開されていないループのループ分析で、オフライン・コンパイラーがループを正常にパイプライン処理したことが示されます。
次の式を検討してみましょう。
kernel void simple_loop (unsigned N, global unsigned* restrict b, global unsigned* restrict c, global unsigned* restrict out) { for (unsigned i = 1; i < N; i++) { c[i] = c[i-1] + b[i]; } out[0] = c[N-1]; }
この図は、オフライン・コンパイラーが並列実行とループパイプライニングを使用してsimple_loopを効率的に実行する方法を示しています。このsimple_loopカーネルのループ解析レポートは、 for.bodyループの場合、 Pipelined列はYesを示し、 II列は1を示します。
クリティカルパスと最大周波数のトレードオフ
可能であれば、オフライン・コンパイラーは与えられたループに対してIIの値1を達成しようと試みます。いくつかのケースでは、オフライン・コンパイラーは、ターゲットfmaxが低下して1になるように努力するかもしれません。
次の式を検討してみましょう。
kernel void nd (global int *dst, int N) { int res = N; #pragma unroll 9 for (int i = 0; i < N; i++) { res += 1; res ^= i; } dst[0] = res; }
次の論理図は、カーネルNDの実際の、より複雑なハードウェア実装の簡略化した表現です。
加算演算とXORゲートによるフィードバックは、オフライン・コンパイラーが目標周波数を達成する能力を制限するクリティカルパスです。結果として得られるHTMLレポートは、クリティカルパスを構成するコントリビュータの内訳をパーセンテージで表したものです。

ループの起動間隔に影響を与えるループキャリー依存関係
ループがパイプライン化されているにもかかわらず、IIの値が1にならない場合があります。これらのケースは、通常、データ依存性またはループ内のメモリー依存性によって発生します。
データ依存とは、ループ反復で以前の反復に依存する変数を使用する状況を指します。この場合、ループはパイプライン化できますが、そのII値は1より大きくなります。次の例を検討してください。
1 // An example that shows data dependency 2 // choose(n, k) = n! / (k! * (n-k)!) 3 4 kernel void choose( unsigned n, unsigned k, 5 global unsigned* restrict result ) 6 { 7 unsigned product = 1; 8 unsigned j = 1; 9 10 for( unsigned i = k; i <= n; i++ ) { 11 product *= i; 12 if( j <= n-k ) { 13 product /= j; 14 } 15 j++; 16 } 17 18 *result = product; 19 }
すべてのループ反復において、カーネルchooseにおけるproduct変数の値は、インデックスiの現在の値に前回の反復からのproductの値を掛けて計算されます。その結果、現在の反復が処理を終了するまで、ループの新しい反復を開始することはできません。下の図は、システムビューアに表示されるカーネルchooseの論理ビューを示しています 。

カーネル選択のループ分析レポートは、ブロック1のII値が13であることを示します。さらに、詳細ペインでは、高II値が製品へのデータ依存によって発生し、クリティカルパスへの最大貢献者は整数13行目の除算演算。


メモリー依存とは、前のループ反復からのメモリーアクセスが完了するまで、ループ反復におけるメモリーアクセスが進まない状況を指す。次の例を検討してください。
1 kernel void mirror_content( unsigned max_i, 2 global int* restrict out) 3 { 4 for (int i = 1; i < max_i; i++) { 5 out[max_i*2-i] = out[i]; 6 } 7 }

カーネルmirror_contentのループ解析では、詳細ペインはメモリー依存のソースと、ブロック2のクリティカルパスへのコントリビュータの割合(%)を示します。
