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