主机和GPU上添加CUDA的不同结果

主机和GPU上添加CUDA的不同结果,cuda,Cuda,我有一个函数,它可以拍摄一张彩色照片并返回它的灰色版本。 如果我在主机上运行顺序代码,一切都会正常工作。如果我在设备上运行它,结果会略有不同(1000个像素中有一个与正确值相比是+1或-1) 我认为这与转换有关,但我不确定。这是我使用的代码: __global__ void rgb2gray_d (unsigned char *deviceImage, unsigned char *deviceResult, const int height, const int width){

我有一个函数,它可以拍摄一张彩色照片并返回它的灰色版本。 如果我在主机上运行顺序代码,一切都会正常工作。如果我在设备上运行它,结果会略有不同(1000个像素中有一个与正确值相比是+1或-1)

我认为这与转换有关,但我不确定。这是我使用的代码:

    __global__ void rgb2gray_d (unsigned char *deviceImage, unsigned char *deviceResult, const int height, const int width){
    /* calculate the global thread id*/
    int threadsPerBlock  = blockDim.x * blockDim.y;
    int threadNumInBlock = threadIdx.x + blockDim.x * threadIdx.y;
    int blockNumInGrid   = blockIdx.x  + gridDim.x  * blockIdx.y;

    int globalThreadNum = blockNumInGrid * threadsPerBlock + threadNumInBlock;
    int i = globalThreadNum;

    float grayPix = 0.0f;
    float r = static_cast< float >(deviceImage[i]);
    float g = static_cast< float >(deviceImage[(width * height) + i]);
    float b = static_cast< float >(deviceImage[(2 * width * height) + i]);
    grayPix = (0.3f * r) + (0.59f * g) + (0.11f * b);

    deviceResult[i] = static_cast< unsigned char > (grayPix);
}

void rgb2gray(unsigned char *inputImage, unsigned char *grayImage, const int width, const int height, NSTimer &timer) {

    unsigned char *deviceImage;
    unsigned char *deviceResult;

    int initialBytes = width * height * 3;  
    int endBytes =  width * height * sizeof(unsigned char);

    unsigned char grayImageSeq[endBytes];

    cudaMalloc((void**) &deviceImage, initialBytes);
    cudaMalloc((void**) &deviceResult, endBytes);
    cudaMemset(deviceResult, 0, endBytes);
    cudaMemset(deviceImage, 0, initialBytes);

    cudaError_t err = cudaMemcpy(deviceImage, inputImage, initialBytes, cudaMemcpyHostToDevice);    

    // Convert the input image to grayscale 
    rgb2gray_d<<<width * height / 256, 256>>>(deviceImage, deviceResult, height, width);
    cudaDeviceSynchronize();

    cudaMemcpy(grayImage, deviceResult, endBytes, cudaMemcpyDeviceToHost);

    ////// Sequential
    for ( int y = 0; y < height; y++ ) {
             for ( int x = 0; x < width; x++ ) {
                   float grayPix = 0.0f;
                   float r = static_cast< float >(inputImage[(y * width) + x]);
                   float g = static_cast< float >(inputImage[(width * height) + (y * width) + x]);
                   float b = static_cast< float >(inputImage[(2 * width * height) + (y * width) + x]);

                   grayPix = (0.3f * r) + (0.59f * g) + (0.11f * b);
                   grayImageSeq[(y * width) + x] = static_cast< unsigned char > (grayPix);
              }
        }

    //compare sequential and cuda and print pixels that are wrong
    for (int i = 0; i < endBytes; i++)
    {
        if (grayImage[i] != grayImageSeq[i])
        cout << i << "-" << static_cast< unsigned int >(grayImage[i]) <<
                 " should be " << static_cast< unsigned int >(grayImageSeq[i]) << endl;
        }

    cudaFree(deviceImage);
    cudaFree(deviceResult);
}
\uuuu全局\uuuuu无效rgb2gray\d(无符号字符*设备图像,无符号字符*设备结果,常量整数高度,常量整数宽度){
/*计算全局线程id*/
int threadsPerBlock=blockDim.x*blockDim.y;
int threadNumInBlock=threadIdx.x+blockDim.x*threadIdx.y;
int blockNumInGrid=blockIdx.x+gridDim.x*blockIdx.y;
int globalThreadNum=blocknumgrid*threadsPerBlock+threadnumblock;
int i=全局线程数;
浮动灰度pix=0.0f;
浮动r=静态施法<浮动>(设备图像[i]);
浮动g=静态施法(设备图像[(宽度*高度)+i]);
浮动b=静态施法(设备图像[(2*宽度*高度)+i]);
灰度指数=(0.3f*r)+(0.59f*g)+(0.11f*b);
deviceResult[i]=静态\u转换(灰度像素);
}
void rgb2gray(无符号字符*输入图像、无符号字符*灰度图像、常量整型宽度、常量整型高度、计时器和计时器){
未签名字符*设备图像;
未签名字符*deviceResult;
int initialBytes=宽度*高度*3;
int endBytes=宽度*高度*大小(无符号字符);
无符号字符grayImageSeq[endBytes];
cudamaloc((void**)和设备映像,initialBytes);
Cudamaloc((void**)和deviceResult,endBytes);
cudaMemset(deviceResult,0,endBytes);
cudaMemset(设备图像,0,初始字节);
cudaError\u t err=cudaMemcpy(设备映像、输入映像、初始字节、cudaMemcpyHostToDevice);
//将输入图像转换为灰度
rgb2gray_d(设备图像、设备结果、高度、宽度);
cudaDeviceSynchronize();
cudaMemcpy(灰度图像、deviceResult、endBytes、cudaMemcpyDeviceToHost);
//////连续的
对于(int y=0;y(输入图像[(y*宽度)+x]);
浮动g=静态施法(输入图像[(宽度*高度)+(y*宽度)+x]);
浮动b=静态施法(输入图像[(2*宽度*高度)+(y*宽度)+x]);
灰度指数=(0.3f*r)+(0.59f*g)+(0.11f*b);
grayImageSeq[(y*宽度)+x]=静态_转换(grayPix);
}
}
//比较顺序和cuda以及错误的打印像素
对于(int i=0;icout浮点运算可以在设备代码和主机代码中产生稍微不同的结果

<> p>有多种可能的情况。你必须考虑这两个函数是由两个不同的编译器编译成在两个不同的浮点硬件实现上运行的两个不同的二进制程序。 例如,如果浮点计算以不同的顺序执行,舍入错误可能会导致不同的结果

此外,在x86体系结构CPU上使用32位(浮点)或64位(双精度)浮点表示形式运行浮点计算时,浮点数学由内部使用80位精度的FPU单元完成,然后,浮点数据类型的结果被截断回32位,双精度数据类型的结果被截断回64位

GPU的ALU使用32位精度进行浮点运算(假设您使用的是浮点数据类型)


可以找到一篇讨论浮点表示和算术的优秀文章。

最后我找到了答案。CUDA会自动进行单精度和双精度的融合乘法加法。使用下面的文档,第4.4节,我设法解决了它。而不是

grayPix = (0.3f * r) + (0.59f * g) + (0.11f * b);
我现在正在做什么

grayPix = __fadd_rn(__fadd_rn(__fmul_rn(0.3f, r),__fmul_rn(0.59f, g)), __fmul_rn(0.11f, b));
这将禁用乘法和加法与融合乘法加法指令的合并


什么是“略有不同”意思是?这意味着,与预期值相比,1000个像素中大约有一个像素是+1或-1。感谢您的回复!我想知道在我的代码中避免这种情况的最佳方法是什么?我几乎尝试了所有方法!)Thnx@andreea.sandu你到底想避免什么?你要求两个结果必须绝对相等吗所以必须注意两个浮点数的直接比较,比如grayImage[i]!=grayImageSeq[i]这被认为是一种不好的做法,应该避免,除非你确信准确的比较确实是你想要的。看看这个,了解一些如何正确进行比较的想法:@andreea.sandu在图像处理中,像这样的小东西应该无关紧要。如果你真的需要“正确”的结果,请对临时值使用双精度(在cpu和gpu上)。实际上,在这一步之后,我计算了一个直方图,因此我计算每个值的像素数。这就是为什么我需要这些数字完全相同。造成差异的最可能原因是“grayPix”计算在主机上的执行精度高于单个精度。为了防止出现这种情况,请声明一些主机代码中的“volatile float”变量,然后将整个计算分解为一系列具有两个输入的单独操作,每个操作的结果都将发送到volatile float目标。例如:{volatile float t0,t1,t2;t0=0.3fr;t1=0.59fg;t2=0.11f*b;t0=t0+t1;t0=t0+t2;grayPix=t0;}当我在主机系统上为GPU设计代码时,我一直在使用这种技术。工作起来很有魅力。非常感谢你的回答,rn修饰符帮助我修复了不同的CPU和GPU结果