如何在OpenCL中充分利用SIMD?

use*_*438 8 gpgpu simd opencl spmd

Beignet优化指南中,这是一个针对英特尔GPU的OpenCL开源实现

工作组大小应大于16并且是16的倍数.

Gen上两个可能的SIMD通道是8或16.为了不浪费SIMD通道,我们需要遵循这个规则.

英特尔处理器显卡Gen7.5计算架构中也提到:

对于基于Gen7.5的产品,每个EU有七个线程,总共28 千字节的通用寄存器文件(GRF).

...

在Gen7.5计算架构中,大多数SPMD编程模型采用这种样式代码生成和EU处理器执行.实际上,每个SPMD内核实例似乎在其自己的SIMD通道内串行且独立地执行.

实际上,每个线程同时执行SIMD-Width数量的内核实例.因此,对于计算 内核 的SIMD-16编译,SIMD-16 x 7线程= 112个内核实例可以在单个EU上同时执行.类似地,对于SIMD-32 x 7个线程= 224个内核实例在单个EU上并发执行.

如果我理解正确,使用SIMD-16 x 7 threads = 112 kernel instances作为示例,为了在一个EU上运行224个线程,工作组大小需要为16.然后OpenCL编译器将16个内核实例折叠到16通道SIMD线程中,并执行此操作在7个工作组中进行7次,并在单个EU上运行它们?

问题1:我是否正确到此为止?

但是OpenCL规范也提供了矢量数据类型.因此,通过传统的SIMD编程(如在NEON和SSE中)充分利用欧盟的SIMD-16计算资源是可行的.

问题2:如果是这种情况,使用vector-16数据类型已经明确使用了SIMD-16资源,因此删除了至少16项每工作组限制.是这样的吗?

问题3:如果以上都是真的,那么两种方法如何相互比较:1) OpenCL编译器将112个线程折叠成7个SIMD-16线程; 2) 7个本机线程编码为明确使用vector-16数据类型和SIMD-16操作?

Tim*_*Tim 1

  1. 几乎。您假设每个工作组有一个线程(NB 线程在这种情况下是 CUDA 所谓的“波”。在 Intel GPU 中,工作项是 GPU 线程的 SIMD 通道)。如果没有子组,就无法强制工作组大小恰好是一个线程。例如,如果您选择 WG 大小为 16,编译器仍然可以自由编译 SIMD8 并将其分布在两个 SIMD8 线程中。请记住,编译器在知道 WG 大小之前就选择了 SIMD 宽度(clCompileProgram之前clEnqueueNDRange)。子组扩展可能允许您强制 SIMD 宽度,但绝对不会在 GEN7.5 上实现。

  2. OpenCL 向量类型是在已自动发生的隐式向量化之上的可选显式向量化步骤。float16例如,您是否使用。每个工作项将分别处理 16 个浮点数,但编译器仍将至少编译 SIMD8。因此,每个 GPU 线程将处理 (8 * 16) 个浮点数(尽管是并行的)。这可能有点矫枉过正了。理想情况下,我们不想通过使用显式 OpenCL 向量类型来显式向量化我们的 CL。但有时如果内核没有做足够的工作(内核太短可能会很糟糕),它会很有帮助。有人说 float4 是一个很好的经验法则。

  3. 我想你的意思是 112 个工作项目?本地线程是指 CPU 线程还是 GPU 线程?

    • 如果您指的是 CPU 线程,则适用有关 GPU 的常见论点。当您的程序差异不大(所有实例都采用相似的路径)并且您使用数据的次数足够多以减轻将数据传输到 GPU 和从 GPU 传输的成本(算术密度)时,GPU 就很好用。
    • 如果您指的是 GPU 线程(GEN SIMD8 或 SIMD16 生物)。目前没有(公开可见)的方法来显式地对 GPU 线程进行编程(编辑请参阅子组扩展(在 GEN7.5 上不可用))。如果可以的话,这将是与汇编语言类似的权衡。这项工作更难,编译器有时会比我们做得更好,但是当您解决特定问题并拥有更好的领域知识时,您通常可以通过足够的编程工作做得更好(直到硬件发生变化和您聪明的程序的假设)就失效了。)