复制到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 %dn", 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.1gpu=GeForce GTX 960M(不是很多,但这不重要(

同样,我是Cuda编程的新手,但我已经尝试了我能想到的,无法孤立问题!谢谢你的帮助。

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

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

一个可能的解决方案是用类似sizeof(unsigned char)的东西来代替sizeof(CV_8U)的使用。由于这个大小是1,您也可以删除sizeof(CV_8U)的乘积,得到相同的行为。

你也可以避免这种分配,让OpenCV为你做分配(和主机设备数据复制(,如这里和这里的答案所示

最新更新