众所周知,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
但 WaveFront 中的项目(线程)之间是否有优化的注册到注册数据交换说明:
例如WARP(CUDA)中的int __shfl_down(int var, unsigned int delta, int width=warpSize);
:https://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/
或x86_64上的__m128i _mm_shuffle_epi8(__m128i a, __m128i b);
SIMD-lanes:https://software.intel.com/en-us/node/524215
例如,这个shuffle-instruction可以执行8个线程/通道的8个元素的Reduce(相加数字), 3个周期,无需任何同步,也不使用任何缓存/本地/共享内存(每次访问的延迟时间约为3个周期)。
即。线程将其值直接发送到其他线程的注册表:https://devblogs.nvidia.com/parallelforall/faster-parallel-reductions-kepler/
或者在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中没有类似的功能:
__ballot(predicate)
__shfl(value, thread)
在版本4,2016年8月28日最终草案OpenCL扩展#35中只有英特尔指定的内置随机播放功能:intel_sub_group_shuffle
,intel_sub_group_shuffle_down
,intel_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 );
要点:
<gentype> sub_group_scan_inclusive_<op>( <gentype> x );
- 函数保持更灵活的功能,并确保线程之间的最快可能通信与直接寄存器到寄存器数据交换。
但是功能shuffle
/ sub_group_broadcast
/ _reduce
并不能保证直接注册到注册数据交换,并且这些子组功能不够灵活
答案 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。
AMD APP SDK支持子组