【问题标题】:What causes this segmentation fault (core dumped) error at cudaMemcpy when copying to GPU?复制到 GPU 时,是什么导致 cudaMemcpy 出现这种分段错误(核心转储)错误?
【发布时间】:2020-10-11 16:09:51
【问题描述】:

在调用 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 运行时,我收到很多 Invalid write of size 8 错误,所有这些都引用 libcuda。通过隔离它,我知道问题出在这个特定的内存副本上。有时它也适用于 valgrind,但我认为这是正常的。我还没有使用 valgrind 的经验,但是内存问题对我来说没有意义(我正在尝试将复制到设备,那么为什么会出现与主机相关的分段错误?)。

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

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

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

【问题讨论】:

  • 这里的问题是您对 OpenCV 的使用,而不是 CUDA。像CV_8U 这样的项目是not a type,它是一个编译器#define。因此sizeof(CV_8U) 没有做你认为它正在做的事情。因此,您对size 的计算是错误的。因此,当cudaMemcpy 操作尝试访问&amp;image.data[0] 以获取size 字节时,您将超出缓冲区的末尾。对于足够大的size 计算(足够大的图像),您将遇到段错误。这基本上与CUDA无关。
  • 作为一个简单的修复方法,只需在您拥有 * sizeof(CV_8U) 的所有位置删除它,您的代码就可以正常工作。
  • 该修复确实有效,并解释了我的代码存在的问题。 sizeof(CV_8U) 的大小为 4,而不是 sizeof(unsigned int) == 1。谢谢!
  • sizeof(unsigned int) 不是 1。
  • 对不起,意思是无符号字符。

标签: c++ cuda segmentation-fault


【解决方案1】:

这里的问题与您对 OpenCV 的使用有关。像CV_8U 这样的项目是not a type,它是一个编译器#define。因此sizeof(CV_8U) 没有做你认为它正在做的事情。您的预期用途应该是捕获底层类型的大小(例如unsigned char,即类型大小为 1)。但是,sizeof(CV_8U) 显然返回了一个整数的大小,即 4。

因此,您对size 的计算是错误的(4 倍太大)。因此,当cudaMemcpy 操作尝试访问&amp;image.data[0] 以获取size 字节时,它将尝试复制超出缓冲区的末尾。对于小图像,溢出不会触发运行时间检查/限制。对于足够大的size 计算(足够大的图像),您将遇到段错误。虽然失败是在 CUDA 调用中触发的,但错误的根源在 CUDA 之外。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-31
    • 2013-11-07
    • 1970-01-01
    • 2022-08-23
    • 1970-01-01
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    相关资源
    最近更新 更多