CUDA处理超大二值图像时网格与块大小配置及性能优化咨询
首先,我们来拆解你的问题核心:你的GTX950M运行这个简单的标注kernel却耗时近600ms,这明显不符合预期——毕竟你的任务只是简单的内存读写+极少量计算,理论上应该是毫秒级的。下面从几个关键维度分析问题并给出优化方案:
1. 最可能的性能瓶颈:数据存储位置错误
你的代码中使用了PtrStepSz,如果传入的src和label是主机内存(比如OpenCV的CPU端Mat),而不是CUDA设备内存,那kernel会通过PCIe总线访问主机内存,这速度比直接访问显存慢一个数量级以上。GTX950M的PCIe 3.0 x16带宽约16GB/s,而显存带宽(GDDR5版本)可达102GB/s,两者差距巨大。
解决方法:
确保数据存放在设备显存中:
- 如果用OpenCV,可以直接使用
cuda::GpuMat来管理设备内存,它会自动完成主机-设备的数据拷贝。 - 原生CUDA的话,用
cudaMalloc分配设备内存,再用cudaMemcpy将主机数据拷贝到设备,处理完成后再拷贝回主机。
2. 计时代码的错误
你的计时逻辑存在问题:
cudaEventRecord(start); labelCUDA <<<gridDim, blockDim>>> (src, label); cudaDeviceSynchronize(); cudaEventRecord(stop);
cudaDeviceSynchronize()会阻塞主机线程直到kernel完成,但cudaEventRecord(stop)应该紧跟在kernel启动之后,再通过cudaEventSynchronize(stop)等待事件完成,这样才能准确测量kernel的执行时间,避免引入主机端的额外阻塞开销。
修正后的计时代码:
cudaEvent_t start, stop; cudaEventCreate(&start); cudaEventCreate(&stop); cudaEventRecord(start); labelCUDA<<<gridDim, blockDim>>>(src, label); cudaEventRecord(stop); cudaEventSynchronize(stop); // 等待事件完成,确保计时准确 float gpuTime; cudaEventElapsedTime(&gpuTime, start, stop); cout << "Gpu used " << gpuTime << " ms." << endl;
3. Block与Grid配置的合理性分析
你的block(64,16)总线程数是1024,刚好是GTX950M(Maxwell架构)每个SM的最大活跃线程数(2048)的一半,每个SM可以同时运行2个这样的block,这个配置本身是合理的,但可以尝试更优的形状:
- 比如
block(32,32):x维度刚好是一个warp(32线程)的大小,内存访问的合并性更好,可能提升缓存命中率。 - 也可以用
cudaOccupancyMaxPotentialBlockSize工具函数,让CUDA自动计算最优的block大小。
Grid的配置(750,128)是刚好覆盖整个图像的,这个没问题,不需要调整。
4. 是否需要拆分图像为小块?
完全不需要。你的图像总数据量(98MB的二值图+393MB的label图)不到500MB,GTX950M的显存至少是2GB,完全可以一次性放下。拆分小块反而会增加数据拷贝和线程调度的额外开销,得不偿失。
优化后的Kernel示例
可以稍微优化一下kernel中的计算,减少重复运算(虽然影响不大,但能让代码更高效):
__global__ void labelCUDA(const PtrStepSz<uint8_t> src, PtrStepSz<int> label) { const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; if (x >= src.cols || y >= src.rows) return; // 提前计算行偏移,避免每个线程重复计算y*src.cols const int row_offset = y * src.cols; if (src(y, x) != 0) { label(y, x) = row_offset + x; } else { label(y, x) = 0; } }
总结
你的核心问题大概率是数据没有放在设备显存中,其次是计时逻辑错误。按照上面的步骤修正后,运行时间应该能降到几十毫秒甚至更低。
内容的提问来源于stack exchange,提问作者fanfan

