CUDA程序调试的一些经验

目录

1. 存储分配检查

2. 变量名检查

3. 核函数输出检查

4. 核函数局部存储空间回收


最近在做一个点云配准的项目,重新把之前就开始玩的CUDA重新拾起来。本来想着稍微改改代码就能够愉快的跑起来,结果改Bug改的我相当上头。结合我之前的帖子和我最近的一些调试经验,总结一个调CUDA程序的一个博客,以方便以后再次遇到类似项目的时候,能够有个参考。简单来说,整个调试可以按照四个步骤来进行,包括存储分配检查,变量名检查,核函数输出检查以及核函数局部存储空间回收。


1. 存储分配检查

为了能够实现并行计算,我们需要把数据从内存移动到显存,并根据显存定义特定的变量名个指针。如果是一维数据相对来说还比较简单,如下所示:

int scale = 100;
std::vector<float> resultMatching(scale, 999.9);
float* resultMatching_Cu;	
cudaMalloc((void**)&resultMatching_Cu, sizeof(float) * resultMatching.size());
cudaMemcpy(resultMatching_Cu, &resultMatching[0], 
sizeof(float) * resultMatching.size(), cudaMemcpyHostToDevice);

这里的resultMatching_Cu就是对应显存的地址,使用cudaMalloc开辟空间,使用cudaMemcpy将数据从内存拷贝到显存,使用cudaMemcpyHostToDevice来指定数据传输的方向。可以看到,仅仅是在显存中定义一个一维数组,就要做这么复杂的操作。这中间只要有一点错误,在核函数中就无法进行正确的计算。最坑的是二维数组,需要更加复杂的操作,有兴趣的朋友可以看下我之前的博客:CUDA如何利用vector实现参数传递

这里对存储的分配非常容易出错,因此我们在对一个存储实体完成对应的空间分配与赋值后,需要进行检查,以及时捕获异常,代码如下:

cudaError_t error_2 = cudaGetLastError();
printf("CUDA error: %s\n", cudaGetErrorString(error_2));

通过对cuda异常情况的分析可以知道在显存分配过程中,是否出现异常。


2. 变量名检查

这里存在一个很容易被忽视的问题。因为我们在定义现存空间的时候,要有一个内存空间的数据容器,将数据拷贝到显存中。一旦调用核函数时,我们使用了内存空间的变量名,而不是显存空间的变量名,那么数据就不能在核函数被获取。由于我们日常养成的编程习惯,导致这样的参数传输错误及容易被忽视。这里有一个小技巧,那就是为显存变量加特定的后缀,以在核函数参数调用的时候进行检查,以避免类似错误的发生。


3. 核函数输出检查

cuda核函数的debug是一个非常上头的过程。原因是我们不能像普通程序那样进行单步调试。这时需要在核函数打印一些数据,以检查数据的计算是否正常,实例如下:

__global__ void CudaTest(
    float** array2D_Cu,
){
   const int tid = threadIdx.x + blockDim.x * blockIdx.x;
   const int tid = threadIdx.x + blockDim.x * blockIdx.x;
   printf("array2D_Cu[0][0]:%f\n", array2D_Cu[10][1]);
}

请注意,这里的输出需要等到核函数执行完,将数据从显存传到内存后,即调用cudaMemcpy (.... cudaMemcpyDeviceToHost), 才能够看到。


4. 核函数局部存储空间回收

有的时候,你的程序测试没有任何问题,在核函数的打印也没有问题,小规模的并行也没有问题。但是,一旦并行规模变大,传回数据就会出现异常,如下如:

这个问题真心把我整上头了。起初,我怀疑是cuda对数组的长度有要求,如果超过界限,就会造成读取存储的错误。因此,当并行数目较小的时候,对应输入的数据也会因为参数的原因而减小了输入的规模。但是搞笑的是,如果我把核函数里面的计算全部注释掉,发现即使问题规模很大,也不会发生读取存储的错误。那么,问题就出现在核函数的计算里面。

那么,最有可能出现问题的地方就是,在核函数分配了存储空间。当并行规模扩大时,由于所有的线程同时在显存上开辟空间,那么就极有可能产生存储溢出,造成访存错误。理论上来说,我们是不应该在核函数再去开辟较多的额外存储空间的。但是如果必须要开辟空间以解决一些较复杂的计算,那么在线程结束的时候一定要释放存储,尽可能防止存储不够分的。在必要的地方添加了delete以回收存储后,上面的问题就解决了:

以上总结了一些我做CUDA程序调试的一些小经验,希望能帮助到想入坑并行编程的同学。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿老甘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值