复制到 GPU 时,是什么导致 cudaMemcpy 出现此分段错误(核心转储)错误?
What causes this segmentation fault (core dumped) error at cudaMemcpy when copying to GPU?
我一直在尝试在调用 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。通过隔离它,我知道问题出在这个特定的 memcopy 上。有时它也适用于 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)
returns看样子是一个整数的大小,也就是4。
因此,您对 size
的计算是错误的(4 倍太大)。因此,当 cudaMemcpy
操作尝试访问 &image.data[0]
的 size
字节时,它将尝试复制超过缓冲区的末尾。对于小图像,over运行 不会触发 运行 时间 check/limit。对于足够大的 size
计算(足够大的图像),您将遇到段错误。虽然失败是在 CUDA 调用中触发的,但错误的根源在 CUDA 之外。
一种可能的解决方案是将您对 sizeof(CV_8U)
的用法替换为类似 sizeof(unsigned char)
的用法。由于该大小为 1,您也可以只删除 sizeof(CV_8U)
的乘法并获得相同的行为。
您也可以避免这种分配,让 OpenCV 为您完成分配(和 host-device 数据复制)工作,如答案 and
中所示
我一直在尝试在调用 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。通过隔离它,我知道问题出在这个特定的 memcopy 上。有时它也适用于 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)
returns看样子是一个整数的大小,也就是4。
因此,您对 size
的计算是错误的(4 倍太大)。因此,当 cudaMemcpy
操作尝试访问 &image.data[0]
的 size
字节时,它将尝试复制超过缓冲区的末尾。对于小图像,over运行 不会触发 运行 时间 check/limit。对于足够大的 size
计算(足够大的图像),您将遇到段错误。虽然失败是在 CUDA 调用中触发的,但错误的根源在 CUDA 之外。
一种可能的解决方案是将您对 sizeof(CV_8U)
的用法替换为类似 sizeof(unsigned char)
的用法。由于该大小为 1,您也可以只删除 sizeof(CV_8U)
的乘法并获得相同的行为。
您也可以避免这种分配,让 OpenCV 为您完成分配(和 host-device 数据复制)工作,如答案