我们可以使用`shuffle()`指令在WaveFront中的项(线程)之间进行reg-to-reg数据交换吗?

时间:2017-02-15 22:06:33

标签: multithreading concurrency opencl gpgpu amd

众所周知,WaveFront(AMD OpenCL)与WARP(CUDA)非常相似:http://research.cs.wisc.edu/multifacet/papers/isca14-channels.pdf

  

GPGPU语言,如OpenCL™和CUDA,被称为SIMT,因为它们   将程序员的线程视图映射到SIMD通道。主题   以锁步方式在同一SIMD单元上执行称为波前   (在CUDA中变形)。

众所周知,AMD建议我们使用本地内存添加(减少)数字。并且为了加速加法(Reduce)建议使用矢量类型:http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/01/AMD_OpenCL_Tutorial_SAAHPC2010.pdf

enter image description here

WaveFront 中的项目(线程)之间是否有优化的注册到注册数据交换说明:

例如,这个shuffle-instruction可以执行8个线程/通道的8个元素的Reduce(相加数字), 3个周期,无需任何同步,也不使用任何缓存/本地/共享内存(每次访问的延迟时间约为3个周期)。

即。线程将其值直接发送到其他线程的注册表:https://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/

enter image description here

或者在OpenCL中,我们只能使用指令gentypen shuffle( gentypem x, ugentypen mask ),它只能用于float16/uint16等矢量类型到每个项目(线程)中,而不能用于WaveFront中的项目(线程)之间:{ {3}}

我们可以使用类似shuffle()的东西来进行WaveFront中项目(线程)之间的reg-to-reg数据交换,这比通过本地内存的数据交换更快吗?

是否存在用于注册到注册数据交换的AMD OpenCL指令,如内部指示__any()__all()__ballot()__shfl() WARP(CUDA):https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/shuffle.html

Warp投票功能:

  • __any(predicate)如果有任何谓词,则返回非零值 warp中的线程返回非零
  • 如果所有谓词都为
  • __all(predicate),则返回非零值 warp中的线程返回非零
  • __ballot(predicate)返回带有相应位的位掩码 在谓词返回非零的情况下设置的线程
  • __shfl(value, thread)从请求的线程返回值 (但只有当这个线程也执行__shfl() - 操作)

结论

众所周知,在OpenCL-2.0中,存在具有类似于WaveFronts的SIMD执行模型的子组:http://on-demand.gputechconf.com/gtc/2015/presentation/S5151-Elmar-Westphal.pdf

对于子组有 - 第160页:Does the official OpenCL 2.2 standard support the WaveFront?

  • int sub_group_all(int predicate)与CUDA相同 - __all(predicate)
  • int sub_group_any(int predicate);与CUDA相同 - __any(predicate)

但是在OpenCL中没有类似的功能:

  • CUDA - __ballot(predicate)
  • CUDA - __shfl(value, thread)

在版本4,2016年8月28日最终草案OpenCL扩展#35中只有英特尔指定的内置随机播放功能:intel_sub_group_shuffleintel_sub_group_shuffle_downintel_sub_group_shuffle_down,{{1 }:http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_OpenCL_Programming_User_Guide2.pdf

同样在OpenCL中有一些函数,通常由shuffle函数实现,但是并不是所有的函数都可以通过使用shuffle函数来实现

  • intel_sub_group_shuffle_up
  • <gentype> sub_group_broadcast( <gentype> x, uint sub_group_local_id );
  • <gentype> sub_group_reduce_<op>( <gentype> x );
  • <gentype> sub_group_scan_exclusive_<op>( <gentype> x );

要点:

  1. <gentype> sub_group_scan_inclusive_<op>( <gentype> x ); - 函数保持更灵活的功能,并确保线程之间的最快可能通信与直接寄存器到寄存器数据交换。

  2. 但是功能shuffle / sub_group_broadcast / _reduce并不能保证直接注册到注册数据交换,并且这些子组功能不够灵活

1 个答案:

答案 0 :(得分:1)

gentype work_group_reduce<op> ( gentype  x)

版本&gt; = 2.0

但它的定义没有说明使用本地内存或寄存器。这只会将每个协作者的x值减少为所有的一个总和。此功能必须由所有工作组项目命中,因此它不在波前级别方法中。此外,不保证浮点运算的顺序。

也许有些供应商注册方式,而有些供应商使用本地内存。 Nvidia确实使用了寄存器。但是一个旧的主流Amd gpu本地内存带宽为3.7 TB / s,这仍然是不错的数量。 (编辑:它不是22 TB / s)对于2k内核,这意味着每个内核每个周期接近1.5个字节,或者每个高速缓存行更快。

对于%100寄存器(如果没有溢出到全局存储器)版本,如果元素数量只有8或16,则可以减少线程数并在线程中进行矢量化缩减而不与其他线程通信。例如

v.s0123 += v.s4567
v.s01 += v.s23
v.s0 += v.s1

应该类似于__m128i _mm_shuffle_epi8及其在CPU上编译时的总和版本,非标量实现将在GPU上使用相同的SIMD来执行这3个操作。

同样使用这些矢量类型往往使用高效的内存事务,即使是全局和本地,而不仅仅是寄存器。

SIMD一次只能在一个波前工作,但波前可能由多个SIMD处理,因此,此向量操作并不意味着正在使用整个波前。或者甚至整个波前可以计算循环中所有向量的第一元素。但对于CPU来说,大多数逻辑选项是逐个SIMD计算工作项(avx,sse),而不是通过相同的索引元素并行计算它们。

如果主要工作组不符合要求,则会生成子内核并使用动态宽度内核进行此类操作。子内核同时在另一个名为sub-group的组上工作。这是在设备端队列中完成的,并且需要OpenCl版本至少为2.0。

http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_OpenCL_Programming_User_Guide2.pdf

中查找“设备端入队”

AMD APP SDK支持子组