多加速器流水线组合 - 2022.1 简体中文

Vitis 统一软件平台文档 应用加速开发 (UG1393)

Document ID
UG1393
Release Date
2022-05-25
Version
2022.1 简体中文

在 VSC 中,您也可以在单一 .xclbin 和运行时环境内创建多个含不同功能的加速器。借助此类组合,您可创建一条包含两个或更多个 VSC 加速器的流水线,这些加速器以流水打拍方式对不同数据集进行操作,如 受支持的平台和启动示例 中的 sysc_compose 示例所示。以下章节中描述了两种可能的使用模型。

ACC1-CPU-ACC2 流水线

此模型定义了任务流水线,其中第一个加速器用于计算中间结果,随后由主机应用对结果加以处理,再由第二个加速器进行进一步处理。第一个加速器的输出缓冲器需经过修改(修改的同时进行复制)后再传递给第二个加速器。代码示例如下所示。

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

此代码在第二个加速器 my_acc2send_while 循环作用域内使用专用 receive_one_in_order API。receive_one_in_orderreceived_one asap API 需要用户定义的 lambda 函数体,如 VPP_ACC 类 API 中所述。

在此例中,my_acc1::receive_one_in_order(或 receive_one_asap)将等待下一项作业按顺序(或尽快)完成,然后执行 lambda 函数体。由于 my_acc2 是从第二个加速器的 send_while 调用的,因此它会以锁步方式计算从第一个加速器 my_acc1 生成的结果。此 API 会返回布尔值,当 send_while 循环已退出且不再接收任何作业时,返回的值为 true。

ACC1-ACC2 流水线

此模式所定义的任务流水线中,第一个加速器的输出缓冲器可供第二个加速器直接使用(无需执行任何主机 CPU 同步)。如 VPP_ACC 类 API 中所述,transfer_buf() API 会提取缓冲池对象并返回对应于正确迭代的缓冲器。通过使用此 API,即可将结果缓冲器从第一个加速器无缝传输到第二个加速器,而无需复制数据。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();