当复制到GPU时,在cudaMemcpy导致此分段错误核心转储错误的原因是什么?

问题描述

我一直在尝试在调用cudamemcpy时使用玩具程序修复分段错误(核心转储)错误消息。它适用于较小的图像,但适用于较大的图像,通常会失败;我之所以说是正常的,是因为在使用valgrind进行调试时,有时它会成功(更多内容请参见下文)。我看过类似的问题,但找不到答案。对不起,如果这是重复的!我只是在学习(并在编写大量并行处理器程序之后)。

这是我的代码,已清理:

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <cuda.h>
#include <iostream>

#include <cuda_runtime.h>
using namespace cv;
using namespace std;

__global__ void
colorToGreyKernel(unsigned char* outPic,unsigned char* inPic,unsigned int width,unsigned int height){
  // printf("trying \n" );

  int Col = blockDim.x * blockIdx.x + threadIdx.x;
  int Row = blockDim.y * blockIdx.y + threadIdx.y;

  if( Col < width && Row < height){

    int greyOffset = Row * width + Col;

    int rgbOffset = greyOffset * 3;

    unsigned char b = inPic[rgbOffset];
    unsigned char g = inPic[rgbOffset +1];
    unsigned char r = inPic[rgbOffset +2];

    outPic[greyOffset] = 0.21f*r + 0.71f*g + 0.07f*b;
  }
}

#define gpuErrchk(ans) { gpuAssert((ans),__FILE__,__LINE__); }
inline void gpuAssert(cudaError_t code,const char *file,int line,bool abort=true)
{
  bool test = code == cudaSuccess;
  // cout << "code " << std::boolalpha<< test;
   if (code != cudaSuccess)
   {
      // const char *errorStr = NULL;
      fprintf(stderr,"GPUassert: %s %s %d\n",cudaGetErrorString(code),file,line);
      if (abort) exit(code);
   }
}

int main(int argc,char** argv )
{
    if ( argc != 2 )
    {
        printf("usage: displayImage.out <Image_Path>\n");
        return -1;
    }
    Mat image;
    unsigned int imsize[2] = {400,400};
    unsigned char* inPic = NULL;
    unsigned char* outPic = NULL;

    gpuErrchk(cudamalloc(&inPic,imsize[0] * imsize[1] * 3 * sizeof(CV_8U)));
    gpuErrchk(cudamalloc(&outPic,imsize[0] * imsize[1] * sizeof(CV_8U)));
    image = imread( argv[1],IMREAD_COLOR );

    resize(image,image,Size(imsize[0],imsize[1]));

    Mat greyImg(image.rows,image.cols,CV_8U,Scalar(125));

    size_t size = image.cols * image.rows * image.channels() * sizeof(CV_8U);
    // This is where it always fails for bigger images
    gpuErrchk(cudamemcpy(inPic,(void*) &image.data[0],size,cudamemcpyHostToDevice));
    gpuErrchk(cudamemcpy(outPic,(void*)&greyImg.data[0],size/3,cudamemcpyHostToDevice));

    dim3 dimGrid(ceil(image.rows/16.0),ceil(image.cols/16.0),1);
    dim3 dimBlock(16,16,1);

    colorToGreyKernel<<<dimGrid,dimBlock>>>(outPic,inPic,(int) image.rows,(int) image.cols);
    cudaDeviceSynchronize();
    gpuErrchk(cudaGetLastError());
    gpuErrchk(cudamemcpy(greyImg.data,outPic,size / 3,cudamemcpyDevicetoHost));

    namedWindow("display Image",WINDOW_AUTOSIZE );
    imshow("display Image",greyImg);

    waitKey(0);
    cudaFree(&inPic[0]);
    cudaFree(&outPic[0]);
    return 0;
  }

我可以在设备上进行分配,但是复制较大的图像会失败。我已经使用opencv :: cuda对其进行了尝试,并且可以加载任何图片并在设备上执行cvtColor而不进行大小调整,因此我得出结论它不是内存(查看nvidia-smi时类似)。

当我使用valgrind运行时,在这一点上,我收到了很多8号错误的无效写入,所有这些都引用了libcuda。我知道是问题所在,是通过隔离它来解决的。有时它也可以在valgrind中使用,但是我已经收集到这很正常。我还没有使用valgrind的经验,但是内存问题对我来说没有任何意义(我试图将复制到设备上,所以为什么与主机相关的分段错误?)。

我的问题很简单,错误从何而来以及如何解决

NVCC = 11.1 gpu = GeForce GTX 960M(不是很多,但这没关系)

再次,我是Cuda编程的新手,但是尝试了我能想到的并且不能隔离问题!感谢您的帮助。

解决方法

这里的问题与您使用OpenCV有关。像CV_8U这样的项目是not a type,它是编译器#define。因此sizeof(CV_8U)并没有按照您认为的去做。您的预期用法应该是捕获基础类型的大小(例如unsigned char,即类型大小为1)。但是,sizeof(CV_8U)显然返回整数的大小,即4。

因此,您对size的计算是错误的(太大了4倍)。结果,当cudaMemcpy操作尝试访问&image.data[0]个字节的size时,它将尝试复制到缓冲区末尾。对于小图像,超限不会触发运行时检查/限制。对于足够大的size计算(足够大的图像),您将遇到段错误。尽管故障是在CUDA调用中触发的,但错误的根源不在CUDA之外。

一种可能的解决方案是将sizeof(CV_8U)的用法替换为sizeof(unsigned char)。由于该大小为1,因此您也可以删除乘以sizeof(CV_8U)的乘法并获得相同的行为。

您还可以避免这种分配,让OpenCV为您完成分配(和主机设备数据复制)工作,如答案herehere

所示