VSC では、1 つの .xclbin およびランタイム環境で異なる機能を持つ複数のアクセラレータを作成することもできます。このような構成を使用すると、サポートされるプラットフォームとスタートアップの例 の sysc_compose
例に示すように、パイプライン (2 つ以上の VSC アクセラレータが異なるデータセットをパイプライン処理) を作成できます。次のセクションでは、使用可能な 2 つのユース モデルについて説明します。
ACC1-CPU-ACC2 パイプライン
このモデルでは、タスクのパイプライン (1 つ目のアクセラレータで中間結果が計算され、それがホスト アプリケーションで処理されたら、2 つ目のアクセラレータでさらに処理される) を定義します。1 つ目のアクセラレータの出力バッファーを変更 (コピー中に変更) してから、2 つ目のアクセラレータに渡す必要があります。コード例は、次のとおりです。
auto inBP = my_acc1::create_bufpool(vpp::input);
auto tmpoBP = my_acc1::create_bufpool(vpp::output);
auto tmpiBP = my_acc2::create_bufpool(vpp::input);
auto outBP = my_acc2::create_bufpool(vpp::output);
my_acc1::send_while(
[=]()->bool {
int* in = my_acc1::alloc_buf<int>(inBP, inSz);
int* tmp = my_acc1::alloc_buf<int>(tmpoBP, tmpSz);
my_acc1::compute(in, tmp, ...);
...;
return ...;
});
my_acc2::send_while(
[=]()->bool {
int* tmp2 = my_acc2::alloc_buf<int>(tmpiBP, tmpSz);
bool cond = my_acc1::receive_one_in_order( // or receive_one_asap
[=]() {
int* tmp1 = my_acc1::get_buf<int>(tmpoBP);
...; // tmp1 -> copy and modify -> tmp2
});
if (!cond) return false;
int* out = my_acc2::alloc_buf<int>(outBP, outSz);
my_acc2::compute(tmp2, out, ...);
...;
return true;
});
my_acc2::receive_all_in_order(
[=]() {
int* out = xfilter1::get_buf<int>(outBP);
...;
});
my_acc1::join();
my_acc2::join();
このコードでは、2 つ目のアクセラレータ my_acc2
の send_while
ループのスコープ内で専用の receive_one_in_order
API を使用します。receive_one_in_order
API または received_one asap
API には、VPP_ACC クラスの API で説明するように、ユーザー定義のラムダ関数本体が必要です。
この場合、my_acc1::receive_one_in_order
(もしくは receive_one_asap
) は、次のジョブが順番に (またはできるだけ早くに) 完了するのを待ってから、ラムダ関数本体を実行します。2 つ目のアクセラレータ my_acc2
の send_while
から呼び出されるので、最初のアクセラレータ my_acc1
から生成された結果を使用してロックステップで計算します。この API はブール値を戻します。この値は、send_while
ループが終了し、受信するジョブがなくなったときに true になります。
ACC1-ACC2 パイプライン
このモデルは、1 つ目のアクセラレータの出力バッファーを 2 つ目のアクセラレータで直接 (ホスト CPU の同期なしで) 使用可能なタスクのパイプラインを定義します。VPP_ACC クラスの API で説明するように、transfer_buf()
API はバッファー プール オブジェクトを受け取り、正しいイテレーションに対応するバッファーを返します。この API を使用すると、データをコピーすることなく、1 つ目のアクセラレータから 2 つ目のアクセラレータへ結果のバッファーをシームレスに転送できます。VSC ランタイムは、アクセラレータおよびジョブ イテレーター間で転送されるこのようなバッファーの再利用を自動的に管理します。
次は、そのコード例です。
auto inBP = my_acc1::create_bufpool(vpp::input);
auto tmpBP = my_acc1::create_bufpool(vpp::remote);
auto outBP = my_acc2::create_bufpool(vpp::output);
my_acc1::send_while(
[=]()->bool {
int* in = my_acc1::alloc_buf<int>(inBP, inSz);
int* tmp = my_acc1::alloc_buf<int>(tmpBP, tmpSz);
my_acc1::compute(in, tmp, ...);
...;
return ...;
});
my_acc2::send_while(
[=]()->bool {
int* tmp;
bool cond = my_acc1::receive_one_in_order( // or receive_one_asap
[=, &tmp]() {
tmp = my_acc1::transfer_buf<int>(tmpBP);
});
if (!cond) return false;
int* out = my_acc2::alloc_buf<int>(outBP, outSz);
my_acc2::compute(tmp, out, ...);
...;
return true;
});
my_acc2::receive_all_in_order(
[=]() {
int* out = xfilter1::get_buf<int>(outBP);
...;
});
my_acc1::join();
my_acc2::join();