这篇文章是分析OpenCV中直方图部分的CUDA代码,代码位于~/opencv-3.4.1/modules/cudaimgproc/src/cuda/hist.cu
,代码量:少。
histogram256Kernel
1 |
|
其中divUp
函数的定义为,其实就是个向上取整的作用,现在我们的grid尺寸依然沿用这种方式:
1 |
|
分析
首先从主机函数下手,看到block和grid的尺寸设计,不禁浮想联翩,为什么要设计成这样(其实blcok的大小刚好为256),为什么不每个线程分配一个像素,也就是thread数目 = 图像尺寸?他这样做意味着一个thread有可能需要处理多个像素,但是毕竟编写的人员肯定是这块的大佬,所以我们慢慢来分析这样做的优势,分析分为两块:存储和逻辑。
我们简要分析一下读取,程序里最大化读取效率,每次读取4个字节,也就是warp的合并访问达到了最大效率—128个字节。存储上,首先每个block计算结果储存到自己的shares memory上,原子加法存在延迟,另外,为了能够每次读取4个字节,程序中使用了类型强制转换,这在GPU上是低效的。
另外在《CUDA并行程序设计》一书中也分析了在这种情况下,原子操作的延迟远大于存储延迟。那么我们怎样才能减少这种延迟呢,那就是使用共享内存,将一部分全局内存存储的工作替换为共享内存的储存。
在计算能力为6.1的设备上发现一次读取4字节的方法比一次单字节读取还慢,而且使用共享内存也比不用慢,应该是图像本来就小(480*640),所以太复杂的逻辑可能导致计算效率下降。
直方图均衡化
OpenCV中的实现各种跳转,均衡化过程非常简单,不知为何长达五六层的调用,而且找到最终的实现,发现一堆模板,重载运算符的,果断放弃,直接给出我在DelataCV中的实现代码。