减少内核排队的开销 - 2023.2 简体中文

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

Document ID
UG1393
Release Date
2023-12-13
Version
2023.2 简体中文

基于 OpenCL 的执行模型支持数据并行和任务并行编程模型。OpenCL 主机通常需要多次调用不同内核。这些内核会在命令队列中按特定顺序排队,或者在无序命令队列中排队。随后,根据计算资源和任务数据可用性,将在器件上调度这些内核的执行。

您可在命令队列上使用 clEnqueueTask 来对内核调用进行排队。分派过程是在主机处理器上执行的。分派会在将内核实参传输到器件上运行的加速器之后调用内核执行。分派器使用低层次 Xilinx Runtime (XRT) 库来传输内核实参并发出开始计算的触发命令。根据为内核设置的实参数量,向加速器分派命令和实参的开销可能在 30 µs 到 60 µs 之间。您可通过最大程度减少内核需执行的次数和最大程度减少 clEnqueueTask 调用次数来降低此开销的影响。理想情况下,应在单次 clEnqueueTask 调用中完成所有计算。

您可通过将数据分批并调用一次内核来最大程度减少 clEnqueueTask 调用次数,并使循环卷绕在原始实现周围以避免多次排队调用的开销。这样还可以提升主机与加速器之间的数据传输性能,因为传输的是少量大数据包,而不是传输大量小数据包。如需了解有关减少内核执行开销的更多信息,请参阅 内核执行

以下示例显示的是用于处理给定工作量或数据大小的简单内核。
#define SIZE 256
extern "C" {
    void add(int *a , int *b, int inc){
        int buff_a[SIZE];
        for(int i=0;i<size;i++)
        {
            buff_a[i] = a[i];
        }
        for(int i=0;i<size;i++)
        {
            b[i] = a[i]+inc;
        }
    }
}
以下示例显示的是与以上示例相同的简单内核,但此处将其优化为处理分批数据。根据 num_batches 实参,内核可在单次调用内处理多项大小为 256 的输入,避免了多次调用 clEnqueueTask 的开销。主机应用可更改为按 SIZE * num_batches 区块来分配数据和缓冲器,实际上即对主机全局存储器与器件存储器之间的存储器分配和数据传输进行分批处理。
#define SIZE 256
extern "C" {
    void add(int *a , int *b, int inc, int num_batches){
        int buff_a[SIZE];
        for(int j=0;j<num_batches;j++)
        {
            for(int i=0;i<size;i++)
            {
                buff_a[i] = a[i];
            }
            for(int i=0;i<size;i++)
            {
                b[i] = a[i]+inc;
            }
       }   
    }
}