问题描述
我一直在尝试在调用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)
的乘法并获得相同的行为。