タスクの並列処理を使用すると、データフロー並列処理の利点を活かすことができます。ループの並列処理とは異なり、タスクの並列処理ではタスク間で発生するバッファリングの利点を活かして、全実行ユニット (タスク) を並列実行できます。
次に例を示します。
void run (ap_uint<16> in[1024],
ap_uint<16> out[1024]
) {
ap_uint<16> tmp[128];
for(int i = 0; i<8; i++) {
processA(&(in[i*128]), tmp);
processB(tmp, &(out[i*128]));
}
}
このコードを実行すると、processA
および processB
関数が順に 128 回実行されます。ループ内の processA
および processB
のレイテンシが合わせて 278 だとすると、総レイテンシは次のように見積もられます。
余分なサイクルはループ設定が原因で、これはスケジュール ビューアーで確認できます。
C/C++ コードでは、タスクの並列処理は for ループに DATAFLOW
プラグマを追加すると実行されます。
#pragma HLS DATAFLOW
OpenCL API コードでは、for ループ前に次の属性を追加します。
__attribute__ ((xcl_dataflow))
このトピックの詳細は、データフロー最適化、HLS プラグマ、および OpenCL 属性 を参照してください。
HLS レポートの見積もりで示したように、タスク間にダブル (ピンポン) バッファーを使用すると、全体的なパフォーマンスを大幅に改善できます。
この場合、異なる反復で異なるタスクが並行実行されるので、デザインの全体的なレイテンシがほぼ 1/2 になります。各関数の処理に 139 サイクルかかり、128 反復が完全にオーバーラップしているので、総レイテンシは次のようになります。
(1x only processA + 127x both processes + 1x only processB) * 139 cycles = 17931 cycles
タスクの並列処理は、インプリメンテーションでパフォーマンスを改善できる効果的な手法ですが、DATAFLOW
プラグマを任意のコードに適用した場合にどれくらい効果的かは大きく異なります。DATAFLOW
プラグマの最終的なインプリメンテーションを理解するには、個々のタスクの実行パターンを確認することが必要です。Vitis コア開発キットでは、同時実行を示す詳細なカーネル トレースが提供されています。
上の図に示すように、この詳細なカーネル トレースでは、データフロー フロー ループの始点が表示されます。プロセス A はループの最初に即座に開始し、プロセス B はプロセス A の終了を待ってから最初の反復を開始します。プロセス B がループの最初の反復を完了している間に、プロセス A は 2 回目の反復の演算を開始します。
この情報のより抽象的な表示は、ホストとデバイスのアクティビティの アプリケーション タイムライン に示されます。