强制CUDA使用寄存器作为变量

时间:2012-08-28 21:41:18

标签: cuda

我的内核中有很多未使用的寄存器。我想告诉CUDA使用一些寄存器来保存一些数据,而不是每次需要时都读取全局数据。 (我无法使用共享内存。)

__global__ void simple(float *gData) {
float rData[1024];
for(int i=0; i<1024; i++) {
  rData[i]=gData[i];
  }
// work on the data here
}

编译w /:nvcc -arch sm_20 --ptxas-options = -v simple.cu,我得到了   0字节堆栈帧,0字节溢出存储,0字节溢出负载
  使用2个寄存器,40个字节cmem [0]

__global__ void simple(float *gData) {
register float rData[1024];
for(int i=0; i<1024; i++) {
  rData[i]=gData[i];
  }
// work on the data here
}

注册声明什么都不做   0字节堆栈帧,0字节溢出存储,0字节溢出负载
  使用2个寄存器,40个字节cmem [0]

__global__ void simple(float *gData) {
volatile float rData[1024];
for(int i=0; i<1024; i++) {
  rData[i]=gData[i];
  }
// work on the data here
}

volatile 声明会创建堆栈存储:
  4096字节堆栈帧,0字节溢出存储,0字节溢出负载
  使用了21个电阻,40个字节的cmem [0]

1)是否有一种简单的方法可以告诉编译器为变量使用寄存器空间?
2)'堆栈帧'在哪里:寄存器,全局存储器,本地存储器,......?什么是堆栈框架? (从什么时候GPU有堆栈?虚拟堆栈?)
3)simple.ptx文件基本上是空的:(nvcc -arch sm_20 -ptx simple.cu)

.loc 2 14 2
ret;

我知道在哪里可以找到真正的机器/编译代码吗?

2 个答案:

答案 0 :(得分:20)

  • 动态索引数组不能存储在寄存器中,因为GPU寄存器文件不能动态寻址。
  • 标量变量由编译器自动存储在寄存器中。
  • 静态索引(即可以在编译时确定索引),数组(例如,少于16个浮点数)可能由编译器存储在寄存器中。

SM 2.0 GPU(Fermi)每个线程最多只支持63个寄存器。如果超过此值,寄存器值将从本地(片外)内存溢出/填​​充,由缓存层次结构支持。 SM 3.5 GPU将每个线程扩展到最多255个寄存器。

一般来说,正如Jared所提到的,每个线程使用太多寄存器是不可取的,因为它减少了占用率,因此降低了内核中的延迟隐藏能力。 GPU在并行性方面茁壮成长,并通过使用其他线程的工作来覆盖内存延迟。

因此,您可能不应该将数组优化为寄存器。相反,请确保跨线程对这些阵列的内存访问尽可能接近顺序,以便最大化合并(即最小化内存事务)。

您提供的示例可能是共享内存的情况,如果

  1. 块中的许多线程使用相同的数据,或
  2. 每线程数组的大小足够小,可以为多个线程块中的所有线程分配足够的空间(每个线程有1024个浮点数)。
  3. 正如njuffa所提到的,你的内核只使用2个寄存器的原因是因为你没有对内核中的数据做任何有用的事情,编译器都消除了死代码。

答案 1 :(得分:3)

如前所述,寄存器(以及PTX&#34;参数空间&#34;)无法动态索引。为了做到这一点,编译器必须发出代码,就像switch...case块一样,将动态索引转换为立即数。我不确定它是自动做的。您可以使用固定大小的元组结构和switch...case来帮助实现。 C / C ++元编程可能是保持这样的代码可管理的首选武器。

此外,对于CUDA 4.0,使用命令行开关-Xopencc=-O3以便除了将普通标量(例如数据结构)映射到寄存器之外的任何内容(请参阅this post)。对于CUDA&gt; 4.0你必须禁用调试支持(没有-G命令行选项 - 只有在禁用调试时才会进行优化。)

PTX级别允许比硬件更多的虚拟寄存器。它们在加载时映射到硬件寄存器。您指定的寄存器限制允许您设置生成的二进制文件使用的硬件资源的上限。它可以作为编译器的启发式工具,以便在编译到PTX时决定何时泄漏(见下文)寄存器,这样可以满足某些并发需求(参见&#34;启动边界&#34;,&#34;占用&#34;和CUDA文档中的&#34;并发内核执行&#34;您可能也喜欢this most interesting presentation)。

对于Fermi GPU,最多有64个硬件寄存器。第64个(或最后一个 - 当使用小于硬件的最大值时)被ABI用作堆栈指针,因此用于&#34;寄存器溢出&#34; (这意味着通过临时将它们的值存储在堆栈中来释放寄存器,并且当需要的寄存器多于可用时会发生这种情况)因此它是不可触及的。