CUDA内核在发布模式下的结果不同

时间:2012-07-26 09:14:42

标签: visual-studio-2010 cuda release-mode

我正在测试CUDA中的一些代码(我是CUDA的新手,这是我的第一个应用程序)。到目前为止,我已经在CUDA中取得了与在CPU上串行运行代码所获得的结果相同的结果。我使用的是Visual Studio 2010,而Build Configuration是Debug。但是一旦我将Build Configuration更改为“Release”,我就会开始得到错误的结果。我现在无法使用Nvidia论坛。拥有CUDA经验的人是否可以指出这个问题。代码如下

__global__ void MyKernel(int *Nptr,int *deltaptr, double *gravityptr, double *separationptr, double *fconptr, double *xForce, double *yForce, double *zForce,
double *xPos, double *yPos, double *zPos )
{
int N = *Nptr;
int delta= *deltaptr;
double gravity= *gravityptr;
double separation = *separationptr;
double fcon = *fconptr;

double len=0.0;
double r12X =0.0;
double r12Y =0.0;
double r12Z =0.0;
double PE=0.0;


int nx = blockDim.x * blockIdx.x + threadIdx.x;//use this place of nx
//int ny = blockDim.x * blockIdx.x + threadIdx.y;//use this place of ny
int ny = blockDim.y * blockIdx.y + threadIdx.y;
//printf("nx:%d ny:%d\n", nx,ny);

if(!(nx< N && ny <N))
    return;
//printf("nx:%d ny:%d\n", nx,ny);


xForce[nx*N+ny] = 0.0;
yForce[nx*N+ny] = -gravity;
zForce[nx*N+ny] = 0.0;

int lowerValuedx = maxOnDevice(nx-delta,0);
int upperValuedx=minOnDevice(nx+delta+1,N);
for(int dx=lowerValuedx; dx<upperValuedx;dx++)
{
    int lowerValuedy=maxOnDevice(ny-delta,0);
    int upperValuedy=minOnDevice(ny+delta+1,N);
    for(int dy=lowerValuedy; dy<upperValuedy;dy++)
    {
        len=sqrt((double)((nx-dx)*(nx-dx)+(ny-dy)*(ny-dy)) ) *separation;
        bool condition = ny!=dy;
        bool condition1 = nx!=dx;

        //if (nx!=dx || ny!=dy)
        if (condition || condition1)
        {
            r12X = xPos[dx*N+dy] - xPos[nx*N+ny];
            r12Y = yPos[dx*N+dy] - yPos[nx*N+ny];
            r12Z = zPos[dx*N+dy] - zPos[nx*N+ny];
            xForce[nx*N+ny] = xForce[nx*N+ny] +fcon*normxOnDevice(r12X,r12Y,r12Z)*(magOnDevice(r12X,r12Y,r12Z)-len);
            yForce[nx*N+ny]= yForce[nx*N+ny] +fcon*normyOnDevice(r12X,r12Y,r12Z)*(magOnDevice(r12X,r12Y,r12Z)-len);
            zForce[nx*N+ny]= zForce[nx*N+ny] +fcon*normzOnDevice(r12X,r12Y,r12Z)*(magOnDevice(r12X,r12Y,r12Z)-len);


        }
    }
}   

}

由于

1 个答案:

答案 0 :(得分:2)

在CPU和GPU结果之间以及GPU上的调试版和发布版之间存在数值差异并不罕见。这并不意味着任何一组结果都不正确,但一个可能比另一个更准确。请参阅NVIDIA的以下白皮书,该白皮书讨论了可能导致数字差异的各种机制:

http://developer.download.nvidia.com/assets/cuda/files/NVIDIA-CUDA-Floating-Point.pdf

您可以检查nvcc标志-fmad = false是否消除了您看到的差异,这表明这些差异是由于FMA / FMAD合并,因此可能无害。

GPU提供FMAD和FMA(融合乘法 - 加法)运算,这些运算将浮点乘法与从属浮点加法组合成单个运算。这有助于提高性能,因为组合操作通常需要与其每个组成部分相同的时间量。但是,任何组合操作的舍入行为都不同于使用两个单独舍入的操作:

单精度FMAD(计算能力<2.0)截断乘法的结果,然后根据IEEE-754舍入或舍入舍入最终加法的结果。相比之下,FMA(计算能力的单精度> = 2.0,双倍精度)计算未连接的双宽产品,将第三个操作数添加到其中,并根据IEEE-754舍入到最接近的总和 - 舍入 - 甚至。由于这种单一的舍入,FMA提供的平均精度优于使用两个单独的舍入操作。 FMA操作在2008版IEEE-754浮点标准中指定。

默认情况下,对于发布版本,CUDA编译器会积极地生成合并操作(FMAD,FMA)以获得最佳性能。换句话说,编译器默认为-fmad = true,它允许编译器合并浮点乘法和加法。通过指定-fmad = false,禁止合并乘法和加法,这通常可以提供与CPU结果更大的一致性,因为大多数CPU不提供FMA操作。显然,禁用合并操作会对性能产生负面影响,因此-fmad = false主要用作健全性检查。

如果怀疑精度问题,我通常建议与更高精度的参考实现(例如基于四精度或双倍技术的实现)进行比较,以准确评估CPU和GPU上的错误,而不是使用CPU版本作为参考(因为CPU结果也受到舍入误差的影响)。