Vitis™ ハードウェア アクセラレーション チュートリアルxilinx.com の Vitis™ 開発環境を参照 |
モジュール 4 のコードおよびファイル (モジュール 1 と同じ手順で Vitis、Vitis アナライザー、および Vitis HLS を設定)
このモジュールの内容
1. テンプレート化された関数を使用し、適用されたプログラマブル係数で計算ループを複製
2. Vitis HLSdataflow
プラグマを使用
3. フル コンパイルを実行してカードをプログラム
ここをクリックして展開し、dataflow
プラグマの詳細をご覧ください。
DATAFLOW
プラグマは、タスク レベルのパイプライン処理をイネーブルにして関数およびループをオーバーラップできるようにし、レジスタ トランスファー レベル (RTL) インプリメンテーションでの同時実行性を増加してデザイン全体のスループットを向上します。
C 記述では、すべての演算が順次に実行されます。pragma HLS allocation などのリソースを制限する指示子を指定しない場合は、Vivado 高位合成 (HLS) ではレイテンシを最小限に抑え、同時実行性を向上するように処理されます。ただし、データ依存性のためにこれが制限されることがあります。たとえば、配列にアクセスする関数またはループは、完了する前に配列への読み出し/書き込みアクセスをすべて終了する必要があります。そのため、そのデータを消費する次の関数またはループの演算を開始できません。DATAFLOW
データ最適化を使用すると、前の関数またはループがすべての演算を完了する前に、次の関数またはループの演算を開始できるようになります。
DATAFLOW
プラグマを指定した場合、HLS ツールで順次関数またはループ間のデータフローが解析され、プロデューサー関数またはループが完了する前にコンシューマー関数またはループの演算を開始できるように、ピンポン RAM または FIFO に基づいてチャネルが作成されます。これにより関数またはループを並列実行でき、レイテンシが削減されて RTL のスループットが向上します。
開始間隔 (II) (関数またはループの開始から次の関数またはループの開始までのサイクル数) が指定されていない場合は、HLS ツールで開始間隔が最小になるようにし、データが使用可能になったらすぐに演算を開始できるようにすることが試みられます。
ヒント: config_dataflow コマンドは、dataflow
最適化で使用されるデフォルトのメモリ チャネルと FIFO の深さを指定します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の config_dataflow に関する説明を参照してください。DATAFLOW
最適化が機能するようにするには、デザイン内でデータが 1 つのタスクから次のタスクに流れる必要があります。次のコーディング スタイルを使用すると、HLS ツールで DATAFLOW
最適化が実行されなくなります。
シングル プロデューサー コンシューマー違反
タスクのバイパス
タスク間のフィードバック
タスクの条件付き実行
複数の exit 条件を持つループ
重要: これらのコーディング スタイルのいずれかが使用されている場合、HLS ツールでメッセージが表示され、DATAFLOW
最適化は実行されません。
STABLE
プラグマを使用して DATAFLOW
領域内の変数を安定とマークすると、変数が同時に読み出しまたは書き込みされるのを回避できます。
最後に、DATAFLOW
最適化には階層インプリメンテーションはありません。サブ関数またはループに最適化が有益な可能性のあるタスクが含まれる場合、最適化をそのループまたはサブ関数に適用するか、サブ関数をインライン展開する必要があります。
構文
C ソースの領域、関数、またはループ内に配置します。
#pragma HLS DATAFLOW
例
次の例では、wr_loop_j ループ内で DATAFLOW
最適化を指定しています。
wr_loop_j: for (int j = 0; j < TILE_PER_ROW; ++j) { #pragma HLS DATAFLOW wr_buf_loop_m: for (int m = 0; m < HEIGHT; ++m) { wr_buf_loop_n: for (int n = 0; n < WIDTH; ++n) { #pragma HLS PIPELINE // should burst WIDTH in WORD beat outFifo >> tile[m][n]; } } wr_loop_m: for (int m = 0; m < HEIGHT; ++m) { wr_loop_n: for (int n = 0; n < WIDTH; ++n) { #pragma HLS PIPELINE outx[HEIGHT*TILE_PER_ROW*WIDTH*i+TILE_PER_ROW*WIDTH*m+WIDTH*j+n] = tile[m][n]; } } }
コレスキー カーネルのコード変更¶
このモジュール 4 では、アルゴリズムのコードを cholesky_kernel.hpp
ヘッダーファイルに移動します。
並列処理と並列計算の数は明示的に指定されるようになっています。これは、NCU
(#define NCU 16
で cholesky_kernel.cpp
に設定された定数) で決まります。
NCU
は、テンプレート パラメーターとして chol_col_wrapper
関数に渡されます (次を参照)。DATAFLOW
プラグマが、chol_col
ループを 16 回呼び出すループに適用されます。
template <typename T, int N, int NCU>
void chol_col_wrapper(int n, T dataA[NCU][(N + NCU - 1) / NCU][N], T dataj[NCU][N], T tmp1, int j)
{
#pragma HLS DATAFLOW
Loop_row:
for (int num = 0; num < NCU; num++)
{
#pragma HLS unroll factor = NCU
chol_col<T, N, NCU>(n, dataA[num], dataj[num], tmp1, num, j);
}
}
DATAFLOW
が確実に適用されるように、dataA は複数の NCU
部分に分割されます。
最後に、ループは NCU
係数を使用して展開されます。この係数は、データのチャンクが処理されるたびに、chol_col
のコピーが NCU
(たとえば 16) 個作成されることを意味します。
デザインの実行¶
モジュール 1 と同じ手順:
ハードウェア エミュレーションを実行します。
Vitis アナライザーを実行します。
Vitis HLS を実行し、結果をデータフロー ビューアーを確認します。これは、合成サマリ レポートから dataflow が適用される関数 (
chol_col_wrapper
) を右クリックすると表示できます。このアニメーションを参照して、アクセス方法および複製が適用されたことを確認する方法を確認してください。
結果のサマリ¶
CPU(*) での実行を含む、すべてのモジュールの実行を比較した実際の結果は、次のようになります。
モジュール | CPU | モジュール 1 | モジュール 2 | モジュール 3 | モジュール 4 (NCU = 16) |
---|---|---|---|---|---|
実行時間 (µs) | 21461 | 793950 | 793732 | 536784 | 11698 |
速度アップ (CPU 基準) | 1 | 0.03x | 0.03x | 0.04x | 1.83x |
速度アップ | なし | 1 | 1 | 1.48x | 68x |
(*): 基準 CPU は、Intel® Xeon® Processor E5-2640 v3 (Nimbix で利用可能) です。