Intel® FPGA SDK for OpenCL™ Pro Edition: 最佳实践实践指南

ID 683521
日期 9/26/2022
Public
文档目录

1.3. 单个work-item Kernel与NDRange Kernel

Intel® 建议在可能的情况下将您的OpenCL kernel构建成单个work-item。 但是如果您的kernel程序得益于显式描述的多并发线程,您就可将您的应用程序构建成NDRange kernel,因为此kernel可并发执行多个work-item。

当kernel描述单个work-item时, Intel® FPGA SDK for OpenCL™ host可以将kernel作为单个work-item来执行,这就相当于启动一个NDRange大小(1, 1, 1)的kernel。编译器尝试加速单个work-item来获得最佳性能。

OpenCL Specification version 1.0中将这种操作模式描述为task parallel programming(任务并行编程)。task是指通过包含一个work-item的工作组执行kernel。

通常,host并行启动多个work-item。但是,该数据并行编程模型不适用于并行work-item之间必须共享细粒度(fine-grained)数据的情况。该情况下,您可以通过将您的kernel表述为单个work-item来最大化吞吐量。不同于NDRange kernel,单个work-item kernel遵循类似于C编程的自然顺序模型。尤其是您不必对work-item之中的数据分区。

为确保FPGA上高吞吐量单个基于work-item的kernel执行, Intel® FPGA SDK for OpenCL™ Offline Compiler必须在任何给定时间并行处理多个流水线阶段。该并行性(parallelism)是通过流水线化循环的迭代来实现。

请参考如下简单实例代码,它显示为单个work-item的累加:

1 kernel void accum_swg (global int* a, 
                         global int* c, 
                         int size, 
                         int k_size) {
2     int sum[1024];
3     for (int k = 0; k < k_size; ++k) {
4        for (int i = 0; i < size; ++i) {
5            int j = k * size + i;
6            sum[k] += __prefetching_load(&a[j]);
7        }
8     }
9     for (int k = 0; k < k_size; ++k) {
10       c[k] = sum[k];
11    }
12 }
每次循环迭代期间,从全局存储器a来的数据值被累加到sum[k]。该实例中,第4行上内部循环的启动间隔值为1,以及延迟为11。而外部循环也有一个启动间隔值大于或等于1,以及延迟为8。
注: 新循环迭代的启动频率被称为启动间隔(initiation interval或者II)。II指的是流水线在可以处理下一个循环迭代之前必须等待的硬件时钟周期数。最佳展开循环的II值为1,因为每个时钟周期处理一个循环迭代。
图 7. 单个work-item Kernel的系统视图

下图说明i的每次迭代如何进入块:

图 8. 内部循环accum_swg.B2执行

您在观察外部循环时会看到,II值为1也意味着线程的每次迭代可在每个时钟周期进入。在本实例中,认为k_size为20和size为4。这样对于首8个时钟周期来说是正确的,因为外部循环迭代0到7可以进入,而且无任何下游(downstream)中止它。一旦 线程0进入内部循环,它需要4次迭代才能完成。线程1到8无法进入内部循环,并且它们会被线程0停顿4个周期。线程1在线程0的迭代完成后才进入内部循环。因此, 线程9在时钟周期13进入外循环。线程9到20每四个时钟周期进入循环, 这个由size的值决定。通过该实例,您可以观察到外循环的动态启动间隔大于静态预测的启动间隔1,并且它是内循环的行程计数的函数。

图 9. 单个Work-Item执行
重要:
  • 使用以下函数中的任意一个会导致您的kernel被解释为NDRange:
    • get_local_id()
    • get_global_id()
    • get_group_id()
    • get_local_linear_id()
    • barrier
  • 如果reqd_work_group_size属性被指定成(1, 1, 1)以外的任何值,您的kernel就将被解释成NDRange。否则,您的kernel将被解释为single-work-item kernel。

请考虑用同一累加实例编写的NDRange:

kernel void accum_ndr (global int* a, 
                       global int* c, 
                       int size) {
   int k = get_global_id(0);

   int sum[1024];
   for (int i = 0; i < size; ++i) {
     int j = k * size + i;
     sum[k] += a[j];
   }
   c[k] = sum[k];
}
图 11. NDRange Kernel的系统视图

限制

OpenCL任务并行编程模型不支持notion笔记中single-work-item执行中的barrier。请使用memory fence(mem_fence)替代您内核中的barrier(barrier