我是CUDA范例的新手。我的问题是确定每个块的线程数和每个网格的块数。有点艺术和试验吗?我发现很多例子似乎都是为这些东西选择的任意数字。
我正在考虑一个问题,我可以将矩阵 - 任意大小 - 传递给乘法方法。因此,C的每个元素(如在C = A * B中)将由单个线程计算。在这种情况下,您如何确定线程/块,块/网格?
答案 0 :(得分:82)
通常,您希望调整块/网格的大小以匹配您的数据,同时最大化占用率,即一次激活多少个线程。影响占用率的主要因素是共享内存使用情况,寄存器使用情况和线程块大小。
支持CUDA的GPU将其处理能力分为SM(流式多处理器),SM的数量取决于实际的卡,但为了简单起见,我们将专注于单个SM(它们都表现相同) 。每个SM具有有限数量的32位寄存器,共享存储器,最大活动块数和最大活动线程数。这些数字取决于您的GPU的CC(计算能力),可以在维基百科文章http://en.wikipedia.org/wiki/CUDA的中间找到。
首先,您的线程块大小应始终是32的倍数,因为内核会在warp(32个线程)中发出指令。例如,如果你的块大小为50个线程,那么GPU仍然会向64个线程发出命令,你只是在浪费它们。
其次,在担心共享内存和寄存器之前,请尝试根据与卡的计算能力相对应的最大线程数和块数来调整块的大小。有时候有多种方法可以做到这一点......例如,CC 3.0卡每个SM可以有16个活动块和2048个活动线程。这意味着如果每个块有128个线程,则在达到2048线程限制之前,可以在SM中容纳16个块。如果你使用256个线程,你只能使用8个,但你仍然使用所有可用的线程,并且仍然可以完全占用。但是,如果命中16个块限制,则每个块使用64个线程将仅使用1024个线程,因此只占50%的占用率。如果共享内存和寄存器使用不是瓶颈,那么这应该是您主要考虑的问题(除了您的数据维度)。
关于网格的主题...网格中的块分布在SM上以启动,然后将剩余的块放入管道中。只要SM中有足够的资源来阻止块,就会将块移动到SM中进行处理。换句话说,当SM中的块完成时,新的块将被移入。您可以使得具有较小块(在前一示例中为128而不是256)的参数可以更快地完成,因为特别慢的块将占用更少的资源,但是这非常依赖于代码。
关于寄存器和共享内存,请查看下一步,因为它可能会限制您的占用率。共享内存对于整个SM是有限的,因此尝试使用它的数量允许尽可能多的块仍然适合SM。寄存器使用也是如此。同样,这些数字取决于计算能力,可以在维基百科页面上找到。祝你好运!
答案 1 :(得分:18)
http://developer.download.nvidia.com/compute/cuda/CUDA_Occupancy_calculator.xls
CUDA占用计算器允许您通过给定的CUDA内核计算GPU的多处理器占用。多处理器占用率是活动warp与GPU多处理器上支持的最大warp数之比。设备上的每个多处理器都有一组N个寄存器,可供CUDA程序线程使用。这些寄存器是在多处理器上执行的线程块之间分配的共享资源。 CUDA编译器尝试最小化寄存器使用,以最大化同时在机器中激活的线程块数。如果程序试图启动一个内核,每个线程使用的寄存器次数,线程块大小大于N,则启动将失败...
答案 2 :(得分:15)
除少数例外情况外,每个块应使用一定数量的线程。然后,每个网格的块数由问题大小决定,例如矩阵乘法时的矩阵维数。
选择每个块的线程数非常复杂。大多数CUDA算法都承认了大量的可能性,并且选择的依据是内核运行效率最高的原因。由于线程调度硬件的工作原理,它几乎总是32的倍数,至少是64。第一次尝试的好选择是128或256。
答案 3 :(得分:3)
您还需要考虑共享内存,因为同一块中的线程可以访问相同的共享内存。如果你正在设计需要大量共享内存的东西,那么每个块的更多线程可能是有利的。
例如,就上下文切换而言,32的任何倍数都是相同的。因此对于1D情况,启动1个具有64个线程的块或2个具有32个线程的块,每个块对全局内存访问没有任何区别。但是,如果手头的问题自然地分解为1长度-64向量,那么第一个选项将比第二个选项更好(内存开销更少,每个线程可以访问相同的共享内存)。