针对相对较小的阵列大小,GPU精度问题?

时间:2012-12-03 03:29:06

标签: cuda precision

我有一些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包含最终结果。

1 个答案:

答案 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中使用同步机制时,请确保所有线程都达到相同的障碍点!我觉得这种事情应该产生编译器警告。