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

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

GPGPU languages, like OpenCL™ and CUDA, are called SIMT because they
map the programmer’s view of a thread to a SIMD lane. Threads
executing on the same SIMD unit in lockstep are called a wavefront
(warp in CUDA).

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

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

但是WaveFront中的项目(线程)之间是否有任何优化的寄存器到寄存器数据交换指令:

>例如int __shfl_down(int var,unsigned int delta,int width = warpSize);在WARP(CUDA):https://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/
>或诸如__m128i _mm_shuffle_epi8(__ m128i a,__ m128i b); x86_64:https://software.intel.com/en-us/node/524215上的SIMD-lanes

例如,这个shuffle-instruction可以执行8个线程/通道的8个元素的Reduce(加数),3个周期,没有任何同步,也没有使用任何缓存/本地/共享内存(有3个周期的延迟)每次访问).

即线程将其值直接发送到其他线程的寄存器:https://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/

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

或者在OpenCL中,我们只能使用指令gentypen shuffle(gentypem x,ugentypen mask),它只能用于每个项目(线程)中的矢量类型(例如float16/uint16),而不能用于WaveFront中的项目(线程)之间:https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/shuffle.html

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

AMD OpenCL指令中是否存在用于注册到寄存器数据交换的内部WaveFront,例如指令__any(),__ all(),__ ballot(),__ shfl()用于WARP内部(CUDA):http://on-demand.gputechconf.com/gtc/2015/presentation/S5151-Elmar-Westphal.pdf

翘曲投票功能:

> __any(谓词)如果有任何谓词,则返回非零值
warp中的线程返回非零
> __all(谓词)如果所有谓词都返回非零
warp中的线程返回非零
> __ballot(谓词)返回带有相应位的位掩码
设置谓词返回非零的线程数
> __shfl(value,thread)从请求的线程返回值
(但仅当此线程还执行了__shfl() – 操作)

结论:

众所周知,在OpenCL-2.0中,存在具有类似于WaveFronts的SIMD执行模型的子组:Does the official OpenCL 2.2 standard support the WaveFront?

对于子组,有 – 第160页:http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_OpenCL_Programming_User_Guide2.pdf

> int sub_group_all(int predicate)与CUDA -__ all(谓词)相同
> int sub_group_any(int predicate);与CUDA -__任何(谓词)相同

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

> CUDA -__选票(谓词)
> CUDA -__ shfl(值,线程)

在版本4,2016年8月28日最终草案OpenCL扩展#35中只有Intel指定的内置shuffle函数:intel_sub_group_shuffle,intel_sub_group_shuffle_down,intel_sub_group_shuffle_down,intel_sub_group_shuffle_up:https://www.khronos.org/registry/OpenCL/extensions/intel/cl_intel_subgroups.txt

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

>< 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);
>< gentype> sub_group_scan_inclusive_< op>(< gentype> x);

摘要:

> shuffle-functions保持更灵活的功能,并通过直接寄存器到寄存器数据交换确保线程之间的最快通信.
>但是函数sub_group_broadcast / _reduce / _scan不保证直接寄存器到寄存器的数据交换,并且这些子组函数不太灵活.

最佳答案 有

gentype work_group_reduce<op> ( gentype  x)

对于版本> = 2.0

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

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

对于?0寄存器(如果没有溢出到全局存储器)版本,如果元素数量只有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),而不是通过相同的索引元素并行计算它们.

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

http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_OpenCL_Programming_User_Guide2.pdf中查找“设备端入队”

AMD APP SDK支持Sub-Group

点赞