为什么CUDA对线程使用的寄存器数量进行汇总?

为什么CUDA对线程使用的寄存器数量进行汇总?,cuda,nvidia,Cuda,Nvidia,我正在分析一个内核,它在GTX480中每个线程使用25个寄存器,每个块使用3568字节的共享内存。内核配置为启动16x16个线程,线程缓存首选项设置为共享 根据GTX480的规范,该设备每个SM有32768个寄存器,因此可能会有25个regs x 256个线程/块x 6个块/SM块同时运行 但是,Compute Visual Profiler和Cuda占用率计算器报告,每个SM只有4个区块处于活动状态。我想知道为什么只有4个街区是活动的,而不是像我预期的5个街区 我发现的原因是CUDA将使用的寄

我正在分析一个内核,它在GTX480中每个线程使用25个寄存器,每个块使用3568字节的共享内存。内核配置为启动16x16个线程,线程缓存首选项设置为共享

根据GTX480的规范,该设备每个SM有32768个寄存器,因此可能会有
25个regs x 256个线程/块x 6个块/SM
块同时运行

但是,Compute Visual Profiler和Cuda占用率计算器报告,每个SM只有4个区块处于活动状态。我想知道为什么只有4个街区是活动的,而不是像我预期的5个街区

我发现的原因是CUDA将使用的寄存器数取整为26,在这种情况下,活动块数为4

为什么CUDA会将寄存器的数量汇总?因为每个线程有25个寄存器,每个块有256个线程,所以每个SM最多可以有5个块,这显然是一个优势

环境设置:

Device 0: "GeForce GTX 480"
CUDA Driver Version / Runtime Version          5.0 / 4.0
ptxas info: Compiling entry function '_Z13kernellS_PiS0_iiS0_' for 'sm_20'
ptxas info: Used 25 registers, 3568+0 bytes smem, 80 bytes cmem[0], 16 bytes cmem[2]
0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads
kernel config: 16x16 threads per block
kernel config: cudaFuncCachePreferShared

你没有正确地解释正在发生的事情。这里没有对每个线程的寄存器数量进行舍入,而是对每个扭曲的寄存器数量进行舍入

您的GPU在每个扭曲的基础上分配寄存器,寄存器“页面大小”为64个寄存器(注意,我松散地使用这个术语,我不知道精确的寄存器文件设计)。在您的情况下,一个warp需要25*32=800个寄存器,必须将其四舍五入到最接近的“页面大小”64,即每个warp需要832个寄存器。每个块包含8个扭曲(256个线程),因此每个块需要6656个寄存器。然后,该内核的每个SM的最大块数为32768/6656,四舍五入到最接近的整数,即每个SM 4个块,而不是预期的5个块


因此,非常简短的答案是寄存器文件分配粒度和页面大小决定了在这种情况下每个SM可以运行多少块。

我不是芯片设计人员之一,但我希望这样可以在芯片上节省一些逻辑。Nvidia可能会选择使用专用的乘法器网络来寻址寄存器(而不是使用需要在内核启动时设置的偏移寄存器),在这种情况下,乘法器中消除的每一位都可以节省相当多的逻辑和芯片空间。tera是对的,这正是硬件的工作方式。对于像这样的内核,您可能可以指定-maxregcount=24,编译器将满足它所需的一切。@ArchaeaSoftware,但这可能会将寄存器拆分为本地内存,或者至少是我所期望的。我用这个regcount编译了它,实际上只使用了24个寄存器,没有一个被分割到本地内存。这就是nvcc的神奇行为吗?你可能会这么认为,但是CUDA的代码生成器知道溢出到本地内存的速度非常慢,因此如果你对寄存器计数施加限制,它将很难在不溢出的情况下适应该限制。例如,它可能会去掉一个归纳变量,而做一些额外的计算。完全正确。占用率计算器列出寄存器分配单元大小(对于sm_20为64)和寄存器分配粒度(对于sm_20为每个扭曲)。注意,还有一个共享的mem分配单元大小。