【问题标题】:How is a parallel scan performed on an array with more elements than threads per block?如何对元素多于每个块的线程数的数组执行并行扫描?
【发布时间】:2014-10-11 06:50:51
【问题描述】:
【问题讨论】:
标签:
c++
parallel-processing
cuda
【解决方案1】:
无论有没有共享内存,CUDA 内核都以可以按任何顺序执行的块(线程块)的形式执行。要充分利用硬件,您的内核调用中必须有多个线程块,但这会产生不确定的执行顺序。
因此,跨大型数组工作的扫描算法必须以线程块大小的片段(以某种方式)工作。如果我们有多个线程块,那么一个给定的线程块就无法知道其他线程块是否已经完成了它们对相邻数据的工作。 (是的,有一些人为的机制来允许线程块间通信,但是这些都充满了困难,并不能大规模解决问题。)
这样做的最终结果是,像这样的算法通常意味着某种全局同步,而在任何场景中唯一安全的全局同步是内核启动。线程块可以独立完成它们的一部分工作,但是当需要将线程块的工作拼接在一起时,我们必须等到步骤 A 在所有线程块中完成,然后才能继续执行步骤 B。
因此,我认为您会发现大多数设备范围 扫描算法,包括您链接的第 39 章 GPU Gems 示例,以及 thrust 和 cub 将启动多个内核以完成这项工作,因为内核启动提供了方便的全局同步。
请注意,我们当然可以设计一个具有单独线程块的扫描,“在每个块上工作的元素多于线程数”,但这并不能最终解决我们的问题(除非我们只使用 1 个线程块),因为我们必须启动多个线程块为了充分利用硬件,一般情况下的多个线程块引入了全局同步的必要性。
我提到的cub 和thrust 实现都是开源模板库,所以如果你愿意,你当然可以在那里研究代码(这不是一件小事)。它们确实代表了由 CUDA 专家设计和构建的高质量方法。您还可以使用以下方法在高层次上轻松研究他们的行为:
nvprof --print-gpu-trace ./mycode
要快速了解正在启动的内核数量以及可能发生的数据传输,或者您可以使用可视化分析器 nvvp 来研究这一点。