CUDA学习--内存处理之寄存器(2)

来源:互联网 发布:东北大学网络教育 证书 编辑:程序博客网 时间:2024/05/29 13:09

1. 寄存器

GPU上一个SM可以看成一个多线程的CPU核。一般CPU拥有二、四、八个核。但一个GPU却有N个SM核。但这里需要注意的是,所有的工作都是有SM上的SP(流处理器)处理的。每个核上SP数目不同,因此每个核支持的线程数目也会有很大的不同。事实上,一个GPU设备上的所有SM中活跃的线程数目通常数以万计

与CPU不同,GPU的每个SM(流多处理器)有上千个寄存器。CPU与GPU架构的一个主要区别就是CPU与GPU映射寄存器的方式。CPU通过使用寄存器重命名和栈来执行多线程。为了运行一个新任务,CPU需要进行上下文切换,将当前所有寄存器的状态保存到栈上,然后从栈中恢复当前需要执行的新线程上次的执行状态。这些操作需要花费上百个CPU时钟周期。如果在CPU上开启过多的线程,时间将主要花费在上下文的切换上。因此,如果在CPU上开启过多的线程,有效工作的吞吐量将会快速降低

然而GPU却恰恰相反。GPU利用多线程隐藏了内存获取与指令执行带来的延迟。GPU不使用寄存器重命名机制,而是致力于为每一个线程都分配一个真实的寄存器(毕竟每个SM有上千个)。因此,当需要上下文切换时,所需要的操作就是将指向当前寄存器组的选择器或指针更新,以指向下一个执行的线程束的寄存器组,开销几乎为零。从而可以看出,在GPU上开启过少的线程反而会因为等待内存事务使GPU处于闲置状态。(这里用到的线程束概念,即同时调度的一组线程,包含32个线程)。

每个SM能够调度若干个线程块。在SM层,线程块即若干个线程束的逻辑组。编译时会计算出每个内核线程需要的寄存器数目。所有的线程块都具有相同的大小,并拥有已知的数目的线程,每个线程块需要的寄存器数目也就是已知的和固定的。如果一个内核函数中的每个线程需要的寄存器过多,在每个SM中GPU能调度的线程块的数量就会受到限制,因此总的执行的线程数量也会受到限制。此时,开启的线程数量过少会造成硬件无法被充分利用,性能下降。
举例来说,如果一个应用程序先前使用了4个线程块,现在改用更多的寄存器,可能导致只有3个线程块可供调度,这样GPU的吞吐量将会降低1/4。

如果想使用寄存器来避免其他更慢的内存类型的使用,需要注意有效的使用它们。例如,一个循环根据一些布尔变量依次设置某个值的每一位。高效的方法是将32个布尔变量封装到一个32位的字中,然后解封装。可以写这样的一个循环,每次根据新的布尔变量修改内存中的内容,做移位操作,移至字中正确的位置:

for(int i = 0; i < 31; i++){    packed_result |= (pack_array[i] << i);}

如果变量packed_result存于内存中,则需要32次读/写内存操作。但如果将变量packet_result设置为局部变量,编译器会将其放入寄存器中,在寄存器中而不是在主存中做操作,然后将结果协会主内存中,因此可以节省31次内存读/写操作。

0 0
原创粉丝点击