cudaMemcpy分段错误

我已经被这个错误困扰了很长一段时间,所以我决定把它发布在这里。

调用cudaMemcpy时发生此分段错误:

CurrentGrid->cdata[i] = new float[size]; cudaMemcpy(CurrentGrid->cdata[i], Grid_dev->cdata[i], size*sizeof(float),\ cudaMemcpyDeviceToHost); 

CurrentGridGrid_dev分别指向主机和设备上的一个grid类对象,在这种情况下i = 0。 类成员cdata是一个浮点型指针数组。 对于debugging,就在这个cudaMemcpy调用之前,我打印出Grid_Dev->cdata[i]CurrentGrid->cdata[i]Grid_dev->cdata[i]的每个元素的值以及size一切看起来不错。 但它仍然以“分段错误(核心转储)”结束,这是唯一的错误信息。 cuda-memcheck只给了“进程没有终止成功”。 我目前无法使用cuda-gdb。 任何有关去哪里的build议?

更新 :现在好像我已经解决了这个问题,通过cudaMalloc设备上的另一个浮点指针A和cudaMemcpy将Grid_dev-> cdata [i]的值赋值给A,然后将cudaMemcpy A赋值给host。 所以上面写的代码片段变成:

 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); 

我这样做是因为valgrindpopup了“大小为8的无效读取”,我认为这是指Grid_dev->cdata[i] 。 我用gdb再次检查它,打印出Grid_dev->cdata[i]为NULL的值。 所以我想我不能直接取消引用设备指针,即使在这个cudaMemcpy调用。 但为什么 ? 根据该线程底部的注释,我们应该能够在cudaMemcpy函数中取消引用设备指针。

此外,我不知道如何cudaMalloc和cudaMemcpy的工作,但我认为由cudaMalloc一个指针,说这里,我们实际上分配这个指针指向设备上的某个地址的基本机制。 通过cudaMemcpy将Grid_dev->cdata[i]到A中,如上面修改后的代码,我们重新指定指针A指向数组。 那么,我们是不是失去了A指向cudaMalloced的前一个地址的轨迹? 这可能导致内存泄漏或什么? 如果是的话,我应该如何妥善解决这个问题? 谢谢!

为了参考,我把这个错误发生在下面的完整函数的代码。

非常感谢!

 __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 } 

编辑:这里是来自valgrind的信息,从中我试图追踪发生内存泄漏的地方。

 ==19340== Warning: set address range perms: large range [0x800000000, 0xd00000000) (noaccess) ==19340== Warning: set address range perms: large range [0x200000000, 0x400000000) (noaccess) ==19340== Invalid read of size 8 ==19340== at 0x402C79: CopyDataToHost(param_t, grid*, grid*) (CheckDevice.cu:48) ==19340== by 0x403646: CheckDevice(param_t, grid*, grid*) (CheckDevice.cu:186) ==19340== by 0x40A6CD: main (Transport.cu:81) ==19340== Address 0x2003000c0 is not stack'd, malloc'd or (recently) free'd ==19340== ==19340== ==19340== Process terminating with default action of signal 11 (SIGSEGV) ==19340== Bad permissions for mapped region at address 0x2003000C0 ==19340== at 0x402C79: CopyDataToHost(param_t, grid*, grid*) (CheckDevice.cu:48) ==19340== by 0x403646: CheckDevice(param_t, grid*, grid*) (CheckDevice.cu:186) ==19340== by 0x40A6CD: main (Transport.cu:81) ==19340== ==19340== HEAP SUMMARY: ==19340== in use at exit: 2,611,365 bytes in 5,017 blocks ==19340== total heap usage: 5,879 allocs, 862 frees, 4,332,278 bytes allocated ==19340== ==19340== LEAK SUMMARY: ==19340== definitely lost: 0 bytes in 0 blocks ==19340== indirectly lost: 0 bytes in 0 blocks ==19340== possibly lost: 37,416 bytes in 274 blocks ==19340== still reachable: 2,573,949 bytes in 4,743 blocks ==19340== suppressed: 0 bytes in 0 blocks ==19340== Rerun with --leak-check=full to see details of leaked memory ==19340== ==19340== For counts of detected and suppressed errors, rerun with: -v ==19340== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2) 

我相信我知道问题是什么,但要确认它,查看您正在使用的代码来设置设备上的Grid_dev类将是有用的。

当在设备上使用一个类或其他数据结构,并且该类有指向内存中的其他对象或缓冲区的指针(推测在设备内存中,对于将在设备上使用的类),则使这个顶级类在设备上可用的过程变得更加复杂。

假设我有这样的一个类:

 class myclass{ int myval; int *myptr; } 

我可以在主机上实例化上面的类,然后malloc一个int数组,并将该指针指定给myptr ,一切都会好的。 为了使这个类只能在设备和设备上使用,过程可能类似。 我可以:

  1. cudaMalloc指向将保存myclass设备内存的指针
  2. (可选)使用cudaMemcpy将主机上myclass的实例化对象复制到步骤1中的设备指针
  3. 在设备上,使用mallocnewmyptr分配设备存储

如果我从来不想访问主机上为myptr分配的存储,上面的顺序是好的。 但是,如果我确实希望从主机可见的存储空间,我需要一个不同的顺序:

  1. cudaMalloc指向将保存myclass设备内存的指针,我们称之为mydevobj
  2. (可选)将主机上myclass的实例化对象复制到步骤1中的设备指针mydevobj ,使用cudaMemcpy
  3. 在主机上创build一个单独的int指针,我们称之为myhostptr
  4. 设备上的cudaMalloc int存储为myhostptr
  5. cudaMemcpy将myhostptr指针值从主机指向设备指针&(mydevobj->myptr)

之后,您可以将embedded式指针myptr指向的数据cudaMemcpy myptrmyptr上分配的区域(通过cudaMalloc

请注意,在步骤5中,因为我正在获取此指针位置的地址,所以此cudaMemcpy操作只需要主机上的mydevobj指针(仅在cudaMemcpy操作中有效)。

设备指针myint的值将被正确设置,以执行您正在尝试执行的操作。 如果您希望将cudaMemcpy数据从myint到主机,则可以在任何cudaMemcpy调用中使用指针myhostptr而不是 mydevobj->myptr 。 如果我们尝试使用mydevobj->myptr ,则需要解引用mydevobj ,然后使用它来检索存储在myptr的指针,然后使用该指针作为到/从位置的副本。 在主机代码中这是不可接受的。 如果你尝试这样做,你会得到一个seg故障。 (请注意,通过类比,我的mydevobj就像你的Grid_dev ,我的myptr就像你的cdata

总体而言,这是一个概念,当你第一次遇到这个概念时,需要仔细考虑,所以这样的问题在SO上出现了一些频率。 您可能需要研究其中一些问题以查看代码示例(因为您尚未提供设置Grid_dev代码):

  1. 例子1
  2. 例2
  3. 例3