複数アクセラレータのパイプライン構成 - 2022.1 日本語

Vitis 統合ソフトウェア プラットフォームの資料: アプリケーション アクセラレーション開発 (UG1393)

Document ID
UG1393
Release Date
2022-05-25
Version
2022.1 日本語

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_acc2send_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_acc2send_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();