在CUDA内核中放入for循环

Putting a for loop in a CUDA Kernel

本文关键字:for 循环 CUDA 内核      更新时间:2023-10-16

在内核中放置for循环是一个坏主意吗?
或者这是一件常见的事情?

将循环放入内核中是很常见的。这并不意味着它总是一个好主意,但也不意味着它不是一个好主意。

决定如何有效地分配任务和数据以及利用相关并行性的一般问题是一个非常困难和未解决的问题,特别是当涉及到CUDA时。正在进行积极的研究,以有效地确定(即,不盲目地探索参数空间)如何为给定的核实现最佳结果。

有时候,将循环放入内核中是很有意义的。例如,对具有强数据独立性的大型规则数据结构的许多元素进行迭代计算非常适合包含循环的内核。其他时候,你可能会决定让每个线程处理许多数据点,例如,如果你没有足够的共享内存来为每个任务分配一个线程(当大量线程共享大量数据时,这并不罕见,并且通过增加每个线程完成的工作量,你可以将所有线程的共享数据放入共享内存中)。

你最好的办法是做一个有根据的猜测,测试,概要,并根据需要修改。这里有很多优化的空间……启动参数、全局内存、常量内存和共享内存、保持寄存器数量稳定、确保合并和避免内存库冲突等。如果你对性能感兴趣,你应该在CUDA 4.0文档页面上查看NVIDIA提供的"CUDA C最佳实践"answers"CUDA占用计算器"(如果你还没有)。

如果你注意你的内存访问模式,这通常是可以的。如果for循环将随机访问内存,导致许多未合并的内存读取,它可能会非常慢。

事实上,我曾经有一段代码在CUDA下运行得很慢,因为我天真地在内核中插入了一个for循环。然而,一旦我考虑到内存访问,例如,通过一次加载一个块到共享中,这样每个线程块可以同时从共享中执行for循环的一部分,这要快得多。

  • 处理大数据的基本模式是使用tile方法,其中输入数据被分割,每个线程在其数据块上工作,其中循环是绝对需要的。示例1:如果输入数据是2D矩阵,知道它的行数超过列数,我将使用唯一的网格块索引访问该行,并使用平铺线程索引方法访问列,使用平铺线程索引方法在平铺大小上循环。
    示例2:如果您的线程需要处理进一步计算所需的单个值。(矢量规范化为例)你需要一个平铺的方法,因为只有在块线程可以有效地同步。

只要它不是在顶层,你应该没问题。在顶层这样做会抵消CUDA的所有优势。

正如Dan指出的,内存访问成为一个问题。解决这个问题的一种方法是加载引用内存到共享内存或纹理内存,如果它不适合共享。原因是未合并的全局内存访问非常慢(大约400个时钟周期,而不是共享内存的大约40个时钟周期)。