CUDA:约简还是原子操作?

3
我正在编写一个涉及计算给定矩阵上最大值的CUDA内核,我正在评估可能性。我找到的最佳方法是:
强制每个线程在共享内存中存储一个值,然后使用缩减算法来确定最大值(pro:最小分歧cons:共享内存在2.0设备上仅限于48Kb)
我无法使用原子操作,因为既有读取操作又有写入操作,所以线程不能通过synchthreads同步。
你有其他想法吗?

共享内存限制是一个缺点吗? - Pavan Yalamanchili
7个回答

6
您可能还想使用CUDA Thrust中的减少例程,它是CUDA 4.0的一部分或可以在此处获得
该库由一对nVidia工程师编写,并且与经过大量手动优化的代码相比具有优势。我相信还进行了一些网格/块大小的自动调整。
您可以通过包装原始设备指针轻松地与自己的内核进行接口。
这仅从快速集成的角度来看。有关理论,请参见tkerwin的答案。

2
到目前为止,我建议使用nVIDIA的libcub(由Duane Merill开发)而不是他们的thrust。 - einpoklum

5

这是在CUDA中执行reductions的常规方法。

在每个块内,

1)为每个线程在共享内存中保留一个运行的缩小值。因此,每个线程将从全局内存中读取n(我个人更喜欢16到32之间),个值并更新来自这些值的缩小值

2)在块内执行缩减算法,以获得每个块的最终缩减值。

这样,您不需要更多的共享内存,仅需(线程数)* sizeof(datatye)字节。

由于每个块都有一个缩小值,因此您需要执行第二次缩小传递以获取最终值。

例如,如果您每个块启动256个线程,并且每个线程读取16个值,则您将能够缩小(256 * 16 = 4096)个元素。

因此,给定100万个元素,您需要在第一遍中启动大约250个块,并且在第二个块中仅需要一个。

对于此配置,如果元素数量>(4096)^ 2,则可能需要第三次传递。

您必须注意全局内存读取是否协同。您无法合并全局内存写入,但那是您需要承受的一种性能损失。


4
NVIDIA有一个CUDA演示程序可以进行降维操作:这里。还有一篇相关的白皮书,解释了设计背后的一些动机。

2

我发现这个文档对于学习CUDA并行规约的基础非常有用。虽然它有点老了,但是一定还有其他技巧可以进一步提高性能。


1
实际上,您所描述的问题并不是关于矩阵的。假设矩阵数据在内存中是连续的,输入数据的二维视图并不重要。它只是一个值序列的缩减,其中所有矩阵元素以任何它们出现在内存中的顺序出现。
假设矩阵表示在内存中是连续的,您只需要执行一个简单的缩减操作。目前最好的可用实现 - 据我所知 - 是由nVIDIA的Duane Merill开发的优秀libcub这里是其设备范围内最大计算函数的文档。
请注意,除非矩阵很小,否则在大多数计算中,它只是线程读取数据并更新其自己的线程特定最大值。只有当一个线程完成了对矩阵的大片区域(或者说大步长区域)的读取时,它才会在任何地方写入其本地最大值 - 通常是将其写入共享内存以进行块级归约。至于原子操作,您可能每隔成千上万次矩阵元素读取就会进行一次 atomicMax() 调用。

感谢提供 libcub 的提示。 - masterxilo
@masterxilo:如果你不是太注重矩阵,你也可以看看我的内核集合,它可能不太适合发布级别,但在我看来更加灵活。虽然它不完全是发布级别的代码,但已经经过了很多测试,外部文档还有待完善。 - einpoklum

0
如果你使用的是K20或Titan,我建议使用动态并行:启动一个单线程内核,然后启动#items个工作内核线程来生成数据,接着再启动#items/第一轮缩减因子个线程进行第一轮缩减,并保持启动直到结果出现。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接