我有一些CUDA代码可以执行一些线性代数来反转特殊类型的结构化矩阵。我使用算法的序列化版本的结果来计算RMS误差。错误随着问题规模的增大而增长,这是我所期望的。任何人都可以提供有关为什么会出现这种情况的见解吗?
GPU代码非常天真。这是故意的,我会很快对它进行优化 - 我只是想要一个简单的基线内核来提供正确的结果。
__global__ void levinson_durbin_gpu(TYPE *h0_d, TYPE *h_d, TYPE *v_d, TYPE *x_d, TYPE *y_d, int N) //Naive kernel
{
int j = threadIdx.x;
int i;
__shared__ TYPE hn_1[512];
hn_1[j] = h_d[j];
for(i=1; i<N; i++)
{
if(j < i)
{
TYPE hn = h_d[i];
TYPE yn = y_d[i];
__syncthreads();
//Set up temporary arrays, compute inner products
__shared__ TYPE temp[512]; //Temp for hn_1_J_v
__shared__ TYPE temp2[512]; //Temp for hn_1_J_x
__shared__ TYPE temp3[512]; //Temp for hn_1_v
temp[j] = hn_1[j]*v_d[i-j-1];
temp2[j] = hn_1[j]*x_d[i-j-1];
temp3[j] = hn_1[j]*v_d[j];
__syncthreads();
//Three reductions at once
for(unsigned int s=1; s<i; s*=2)
{
int index = 2*s*j;
if((index+s) < i)
{
temp[index] += temp[index+s];
temp2[index] += temp2[index+s];
temp3[index] += temp3[index+s];
}
__syncthreads();
}
TYPE hn_1_J_v = temp[0];
TYPE hn_1_J_x = temp2[0];
TYPE hn_1_v = temp3[0];
TYPE alpha_v = (hn - hn_1_J_v)/(h0_d[0] - hn_1_v);
TYPE alpha_x = (yn - hn_1_J_x)/(h0_d[0] - hn_1_v);
__shared__ TYPE w_v[512];
w_v[j] = v_d[j] - alpha_v*v_d[i-j-1];
__shared__ TYPE w_x[512];
w_x[j] = x_d[j] - alpha_x*v_d[i-j-1];
v_d[j] = w_v[j];
x_d[j] = w_x[j];
if(j == 0)
{
v_d[i] = alpha_v;
x_d[i] = alpha_x;
}
}
__syncthreads();
}
}
标识符TYPE
是float还是double,具体取决于我编译代码的方式。我正在使用带有N个线程的1个块(再次,这里保持简单和简单)。单精度我看到以下结果:
N = 4:RMS误差= 0.0000000027
N = 8:RMS误差= 0.0000001127
N = 16:RMS误差= 0.0000008832
N = 32:RMS误差= 0.0000009233
N = 64:RMS误差= 42.0136776452
N = 80:RMS误差= 281371.7533760048
我无法判断这是我的算法错误还是某种精确问题。如果有帮助我可以使用双精度,算法的CPU版本或计算RMS错误的代码显示上述结果。我使用的是GeForce GTX 660 Ti(cc 3.0)GPU。变量x_d
包含最终结果。
答案 0 :(得分:1)
感谢评论部分的帮助,我能够自己解决问题,所以我会在这里记录,以防其他人遇到类似的问题。
问题确实是同步问题 - 我在发散控制流程块中使用__syncthreads()
。解决方案是将控制流程块分成多个部分,并在每个部分之后调用__syncthreads()
:
__global__ void levinson_durbin_gpu(TYPE *h0_d, TYPE *h_d, TYPE *v_d, TYPE *x_d, TYPE *y_d, int N) //Naive kernel
{
int j = threadIdx.x;
int i;
__shared__ TYPE hn_1[512];
hn_1[j] = h_d[j];
__syncthreads();
//Set up temporary arrays
__shared__ TYPE temp[512]; //Temp for hn_1_J_v
__shared__ TYPE temp2[512]; //Temp for hn_1_J_x
__shared__ TYPE temp3[512]; //Temp for hn_1_v
TYPE hn;
TYPE yn;
for(i=1; i<N; i++)
{
if(j < i)
{
hn = h_d[i];
yn = y_d[i];
//Compute inner products
temp[j] = hn_1[j]*v_d[i-j-1];
temp2[j] = hn_1[j]*x_d[i-j-1];
temp3[j] = hn_1[j]*v_d[j];
}
__syncthreads();
//Have all threads complete this section to avoid synchronization issues
//Three reductions at once
for(unsigned int s=1; s<i; s*=2)
{
int index = 2*s*j;
if((index+s) < i)
{
temp[index] += temp[index+s];
temp2[index] += temp2[index+s];
temp3[index] += temp3[index+s];
}
__syncthreads();
}
if(j < i)
{
TYPE hn_1_J_v = temp[0];
TYPE hn_1_J_x = temp2[0];
TYPE hn_1_v = temp3[0];
TYPE alpha_v = (hn - hn_1_J_v)/(h0_d[0] - hn_1_v);
TYPE alpha_x = (yn - hn_1_J_x)/(h0_d[0] - hn_1_v);
__shared__ TYPE w_v[512];
w_v[j] = v_d[j] - alpha_v*v_d[i-j-1];
__shared__ TYPE w_x[512];
w_x[j] = x_d[j] - alpha_x*v_d[i-j-1];
v_d[j] = w_v[j];
x_d[j] = w_x[j];
if(j == 0)
{
v_d[i] = alpha_v;
x_d[i] = alpha_x;
}
}
__syncthreads();
}
}
N = 32:RMS误差= 0.0000009233
N = 64:RMS误差= 0.0000027644
N = 128:RMS误差= 0.0000058276
N = 256:RMS误差= 0.0000117755
N = 512:RMS误差= 0.0000237040
我学到了什么:在CUDA中使用同步机制时,请确保所有线程都达到相同的障碍点!我觉得这种事情应该产生编译器警告。