Intel® FPGA SDK for OpenCL™: ベスト・プラクティス・ガイド

ID 683521
日付 12/08/2017
Public
ドキュメント目次

1.3. シングル・ワーク・アイテム・カーネル対NDRangeカーネル

インテル® は、可能であれば、OpenCLカーネルをシングル・ワーク・アイテムとして構成することを推奨します。 ただし、カーネル・プログラムにループとメモリーの依存関係がない場合、カーネルがMultiple Work-Itemsを効率的に並列に実行できるため、アプリケーションをNDRangeカーネルとして構成することができます。

インテル® FPGA SDK for OpenCL™ ホストはSingle Work-Itemとしてカーネルを実行できます。これはNDRangeサイズが(1,1,1)のカーネルを起動するのと同じです。

OpenCL仕様バージョン1.0では、この動作モードがタスク・パラレル・プログラミングとして記述されています。 タスクとは、Single Work-Itemを含む1つのワークグループで実行されるカーネルのことです。

一般に、ホストはMultiple Work-Itemを並行して起動します。しかし、このデータ並列プログラミング・モデルは、並列Work-Item間で細かいデータを共有する必要がある場合には適していません。このような場合、カーネルをSingle Work-Itemとして表現することで、スループットを最大化することができます。 NDRangeカーネルとは異なり、Single Work-Itemカーネルは、Cプログラミングに似た自然なシーケンシャル・モデルに従います。特に、Work-Item間でデータを分割する必要はありません。

FPGA上でのハイスループットのWork-Itemベースのカーネル実行を確実にするために、 Intel® FPGA SDK for OpenCL™オフライン・コンパイラーは、ある時点で複数のパイプライン・ステージを並列に処理する必要があります。この並列性は、ループの反復をパイプライン化することによって実現されます。

Single Work-Itemでの累積を示す次の簡単なコード例を検討してください。

1 kernel void accum_swg (global int* a, global int* c, int size, int k_size) { 2 int sum[1024]; 3 for (int k = 0; k < k_size; ++k) { 4 for (int i = 0; i < size; ++i) { 5 int j = k * size + i; 6 sum[k] += a[j]; 7 } 8 } 9 for (int k = 0; k < k_size; ++k) { 10 c[k] = sum[k]; 11 } 12 }
各ループ反復の間に、グローバルメモリーaからのデータ値は、 sum [k]に蓄積されます。この例では、4行目の内側ループは、開始インターバル値が1で、レイテンシーが11です。外側ループも1以上の開始インターバル値を持ち、レイテンシーは8です。
注: 新しいループ反復の開始頻度は開始間隔(II)と呼ばれます。 IIは、パイプラインが次のループ反復を処理する前に待機しなければならないハードウェア・クロック・サイクルの数を指します。最適にアンロールされたループは、1つのループ反復がクロックサイクルごとに処理されるため、IIの値が1です。
図 6. ループ解析レポート
図 7. Single Work-Itemカーネルのシステムビュー

次の図は、iの各反復がどのようにブロックに入るかを示しています。

図 8. 内部ループaccum_swg.B2実行

外部ループを観測すると、IIの値が1であるということは、スレッドの各反復がすべてのクロックサイクルに入ることもできることを意味します。この例では、k_sizeが20でサイズが4であると見なされます。これは、外側のループ反復0~7がそれをストールさせることなく入り込むことができるので、最初の8クロックサイクルにあてはまります。スレッド0が内部ループに入ると、それは4回の反復で終了します。スレッド1〜8は内部ループに入ることができず、スレッド0によって4サイクル停止します。スレッド0の反復が完了すると、スレッド1は内部ループに入ります。その結果、スレッド9は、クロックサイクル13で外側ループに入ります。スレッド9から20は、 サイズの値である4クロックサイクルごとにループに入ります。この例では、外部ループの動的開始間隔が静的に予測される開始間隔1よりも大きく、内部ループのトリップ数の関数であることがわかります。

図 9. Single Work-Itemの実行
重要:
  • 次の関数のいずれかを使用すると、カーネルがNDRangeとして解釈されます。
    • get_local_id()
    • get_global_id()
    • get_group_id()
    • get_local_linear_id()
    • barrier
  • reqd_work_group_size属性が( 1,1,1)以外の値に指定されている場合、カーネルはNDRangeとして解釈されます。それ以外の場合、カーネルは Single Work-Itemカーネルとして解釈されます。

NDRangeで書かれた同じ累算例を検討してください。

kernel void accum_ndr (global int* a, global int* c, int size) { int k = get_global_id(0); int sum[1024]; for (int i = 0; i < size; ++i) { int j = k * size + i; sum[k] += a[j]; } c[k] = sum[k]; }
図 10. ループ解析レポート
図 11. System View of the NDRange Kernel

制限

OpenCLタスク並列プログラミング・モデルは、 Single Work-Itemの実行におけるバリアの概念をサポートしていません。バリア( barrier)をカーネル内のメモリーフェンス( mem_fence )に置き換えます。