我有一个内核做一个线性最小二乘拟合。事实证明,线程使用的寄存器太多,因此占用率很低。这是内核,
__global__
void strainAxialKernel(
float* d_dis,
float* d_str
){
int i = threadIdx.x;
float a = 0;
float c = 0;
float e = 0;
float f = 0;
int shift = (int)((float)(i*NEIGHBOURS)/(float)WINDOW_PER_LINE);
int j;
__shared__ float dis[WINDOW_PER_LINE];
__shared__ float str[WINDOW_PER_LINE];
// fetch data from global memory
dis[i] = d_dis[blockIdx.x*WINDOW_PER_LINE+i];
__syncthreads();
// least square fit
for (j=-shift; j<NEIGHBOURS-shift; j++)
{
a += j;
c += j*j;
e += dis[i+j];
f += (float(j))*dis[i+j];
}
str[i] = AMP*(a*e-NEIGHBOURS*f)/(a*a-NEIGHBOURS*c)/(float)BLOCK_SPACING;
// compensate attenuation
if (COMPEN_EXP>0 && COMPEN_BASE>0)
{
str[i]
= (float)(str[i]*pow((float)i/(float)COMPEN_BASE+1.0f,COMPEN_EXP));
}
// write back to global memory
if (!SIGN_PRESERVE && str[i]<0)
{
d_str[blockIdx.x*WINDOW_PER_LINE+i] = -str[i];
}
else
{
d_str[blockIdx.x*WINDOW_PER_LINE+i] = str[i];
}
}
我有32x404块,每个块有96个线程。在GTS 250上,SM应能够处理8个块。然而,视觉分析器显示我每个线程有11个寄存器,因此占用率为0.625(每个SM 5个块)。顺便说一下,每个块使用的共享内存是792 B,所以寄存器就是问题所在。 表演不是世界末日。我只是好奇,无论如何我可以解决这个问题。感谢。
答案 0 :(得分:2)
快速但有限的寄存器/共享内存与缓慢但大的全局内存之间总是需要权衡。没有办法“绕过”这种权衡。如果通过使用全局内存来使用reduce寄存器,则应该获得更高的占用率但内存访问速度更慢。
也就是说,这里有一些使用更少寄存器的想法:
a被计算为一个简单的算术序列,所以减少它......(像这样)
a = ((NEIGHBORS-shift) - (-shift) + 1) * ((NEIGHBORS-shift) + (-shift)) / 2
或
a = (NEIGHBORS + 1) * ((NEIGHBORS - 2*shift)) / 2
所以相反,做一些类似下面的事情(你可以进一步减少这些表达式):
str[i] = AMP*((NEIGHBORS + 1) * ((NEIGHBORS - 2*shift)) / 2*e-NEIGHBOURS*f)
str[i] /= ((NEIGHBORS + 1) * ((NEIGHBORS - 2*shift)) / 2*(NEIGHBORS + 1) * ((NEIGHBORS - 2*shift)) / 2-NEIGHBOURS*c)
str[i] /= (float)BLOCK_SPACING;
答案 1 :(得分:2)
占用不是问题。
GTS 250中的SM(计算能力1.1)可以在其寄存器中同时保存8个块(8x96个线程),但它只有8个执行单元,这意味着8x96中只有8个(或者,在您的情况下) ,5x96)线程将在任何给定的时刻推进。尝试将更多块压缩到过载的SM上没有什么价值。
实际上,您可以尝试使用-maxrregcount选项来增加寄存器的数量,这可能会对性能产生积极影响。
答案 2 :(得分:1)
您可以使用启动边界指示编译器为最大线程数生成寄存器映射,并为每个多处理器生成最小数量的块。这可以减少寄存器计数,以便您可以实现所需的占用率。
对于您的情况,Nvidia的占用率计算器显示理论峰值占用率为63%,这似乎是您实现的目标。正如您所提到的,这是由于您的寄存器计数,但也是由于每个块的线程数。将每个块的线程数增加到128并将寄存器计数减少到10会产生100%的理论峰值占用率。
控制内核的启动范围:
__global__ void
__launch_bounds__(128, 6)
MyKernel(...)
{
...
}
然后只需使用128个线程的块大小启动并享受您的入住率。编译器应生成内核,使其使用10个或更少的寄存器。