C++ cudaMemcpy分段故障
我已经被这个错误困扰了很长一段时间,所以我决定把它贴在这里 调用cudaMemcpy时发生此分段错误:C++ cudaMemcpy分段故障,c++,cuda,segmentation-fault,C++,Cuda,Segmentation Fault,我已经被这个错误困扰了很长一段时间,所以我决定把它贴在这里 调用cudaMemcpy时发生此分段错误: CurrentGrid->cdata[i] = new float[size]; cudaMemcpy(CurrentGrid->cdata[i], Grid_dev->cdata[i], size*sizeof(float),\ cudaMemcpyDeviceToHost); CurrentGrid和Grid\u dev分别是主机和设备上
CurrentGrid->cdata[i] = new float[size];
cudaMemcpy(CurrentGrid->cdata[i], Grid_dev->cdata[i], size*sizeof(float),\
cudaMemcpyDeviceToHost);
CurrentGrid
和Grid\u dev
分别是主机和设备上的Grid
类对象的指针,在此上下文中i=0。类成员cdata是浮点型指针数组。为了进行调试,在调用cudaMemcpy之前,我打印出了Grid\u Dev->cdata[I]
的每个元素的值,以及CurrentGrid->cdata[I]
和Grid\u Dev->cdata[I]
的地址和size
的值,看起来都不错。但它仍然以“分段错误(堆芯转储)”结束,这是唯一的错误消息。cuda memcheck仅给出“进程未成功终止”。我目前无法使用cuda gdb。关于去哪里有什么建议吗
更新:现在我似乎已经解决了这个问题,我在设备上用另一个浮点指针cudaMalloc,然后将Grid\u dev->cdata[I]的值CUDAMMCPY到A,然后将CUDAMMCPY到主机。
因此,上面编写的代码段变成:
float * A;
cudaMalloc((void**)&A, sizeof(float));
...
...
cudaMemcpy(&A, &(Grid_dev->cdata[i]), sizeof(float *), cudaMemcpyDeviceToHost);
CurrentGrid->cdata[i] = new float[size];
cudaMemcpy(CurrentGrid->cdata[i], A, size*sizeof(float), cudaMemcpyDeviceToHost);
我这样做是因为valgrind弹出了“大小为8的无效读取”,我认为这是指Grid\u dev->cdata[I]
。我用gdb再次检查了它,打印出Grid\u dev->cdata[I]
的值为空。因此,我想即使在这个cudaMemcpy调用中,我也不能直接取消对设备指针的引用。但是为什么呢?根据本文底部的注释,我们应该能够在cudaMemcpy函数中取消对设备指针的引用
另外,我不知道cudamaloc和cudaMemcpy如何工作的潜在机制,但我认为通过cudamaloc一个指针,比如这里的a,我们实际上分配这个指针指向设备上的某个地址。通过cudaMemcpy将Grid\u dev->cdata[i]
分配给A,就像上面修改的代码一样,我们将指针A重新分配给数组。那么,我们是不是忘记了A在添加地址时指向的上一个地址?这是否会导致内存泄漏或其他问题?如果是,我应该如何正确处理这种情况?
谢谢
为了便于参考,我将发生此错误的完整函数的代码放在下面
非常感谢
__global__ void Print(grid *, int);
__global__ void Printcell(grid *, int);
void CopyDataToHost(param_t p, grid * CurrentGrid, grid * Grid_dev){
cudaMemcpy(CurrentGrid, Grid_dev, sizeof(grid), cudaMemcpyDeviceToHost);
#if DEBUG_DEV
cudaCheckErrors("cudaMemcpy1 error");
#endif
printf("\nBefore copy cell data\n");
Print<<<1,1>>>(Grid_dev, 0); //Print out some Grid_dev information for
cudaDeviceSynchronize(); //debug
int NumberOfBaryonFields = CurrentGrid->ReturnNumberOfBaryonFields();
int size = CurrentGrid->ReturnSize();
int vsize = CurrentGrid->ReturnVSize();
CurrentGrid->FieldType = NULL;
CurrentGrid->FieldType = new int[NumberOfBaryonFields];
printf("CurrentGrid size is %d\n", size);
for( int i = 0; i < p.NumberOfFields; i++){
CurrentGrid->cdata[i] = NULL;
CurrentGrid->vdata[i] = NULL;
CurrentGrid->cdata[i] = new float[size];
CurrentGrid->vdata[i] = new float[vsize];
Printcell<<<1,1>>>(Grid_dev, i);//Print out element value of Grid_dev->cdata[i]
cudaDeviceSynchronize();
cudaMemcpy(CurrentGrid->cdata[i], Grid_dev->cdata[i], size*sizeof(float),\
cudaMemcpyDeviceToHost); //where error occurs
#if DEBUG_DEV
cudaCheckErrors("cudaMemcpy2 error");
#endif
printf("\nAfter copy cell data\n");
Print<<<1,1>>>(Grid_dev, i);
cudaDeviceSynchronize();
cudaMemcpy(CurrentGrid->vdata[i], Grid_dev->vdata[i], vsize*sizeof(float),\
cudaMemcpyDeviceToHost);
#if DEBUG_DEV
cudaCheckErrors("cudaMemcpy3 error");
#endif
}
cudaMemcpy(CurrentGrid->FieldType, Grid_dev->FieldType,\
NumberOfBaryonFields*sizeof(int), cudaMemcpyDeviceToHost);
#if DEBUG_DEV
cudaCheckErrors("cudaMemcpy4 error");
#endif
}
我相信我知道问题出在哪里,但为了证实这一点,查看用于在设备上设置
Grid\u dev
类的代码将非常有用
当一个类或其他数据结构要在设备上使用时,并且该类中有指向内存中其他对象或缓冲区的指针(对于将在设备上使用的类,可能在设备内存中),则使该顶级类在设备上可用的过程变得更加复杂
假设我有一个这样的类:
class myclass{
int myval;
int *myptr;
}
我可以在主机上实例化上述类,然后malloc
一个int
数组,并将该指针指定给myptr
,一切都会很好。为了使该类仅在设备上可用,该过程可以类似。我可以:
myclass
myclass
实例化对象从步骤1复制到设备指针malloc
或new
为myptr
myptr
分配的存储,那么上面的顺序就可以了。但如果我确实希望从主机上看到该存储,我需要一个不同的序列:
myclass
,我们称之为mydevobj
myclass
实例化对象复制到设备指针mydevobj
myhostptr
int
存储在设备上,用于myhostptr
myhostptr
指针值移动到设备指针&(mydevobj->myptr)
cudaMemcpy
将嵌入指针myptr
指向的数据myptr
分配到myhostptr
上的区域(通过cudamaloc
)
请注意,在步骤5中,由于我正在获取此指针位置的地址,因此此cudaMemcpy操作只需要主机上的mydevobj
指针,该指针在cudaMemcpy操作中有效(仅限)
然后将正确设置设备指针的值myint
,以执行您尝试执行的操作。然后,如果您想将数据从myint
发送到主机,则可以在任何cudaMemcpy调用中使用指针myhostptr
,而不是mydevobj->myptr
。如果我们尝试使用mydevobj->myptr
,则需要取消引用mydevobj
,然后使用它检索存储在myptr
中的指针,然后将该指针用作指向/来自位置的副本。这在主机代码中是不可接受的。如果你尝试这样做,你会得到一个seg故障。(请注意,作为类比,myMyDevBj
类似于您的Grid\u dev
,mymyptr
类似于您的cdata
)
总的来说,这是一个概念,在你第一次遇到它时需要仔细思考,所以像这样的问题会出现一些频率。您可能希望研究其中一些问题以查看代码示例(因为您尚未提供设置Grid\u dev
)的代码:
分段故障意味着主机代码内存访问问题。您不需要cuda memcheck或cuda gdb来诊断根本原因。标准主机调试器或内存分析
class myclass{
int myval;
int *myptr;
}