CUDA小内核2D卷积 - 如何实现

14

我已经尝试了几天使用CUDA内核进行快速的二维卷积,针对一个500x500的图像(但我也可以改变其大小)和一个非常小的二维kernel(拉普拉斯2D kernel,因此是一个3x3的kernel..太小,以至于所有cuda线程都无法大幅度提高效率)。

我创建了一个CPU传统实现(两个for循环,就像您想的那样容易),然后开始创建CUDA内核。

在尝试了几次无果之后,我最终找到了这段代码:http://www.evl.uic.edu/sjames/cs525/final.html (请查看共享内存部分),它基本上让一个16x16的线程块将他所需的所有卷积数据加载到共享内存中,然后执行卷积。

然而,CPU仍然要快得多。我没有尝试FFT方法,因为CUDA SDK指出它仅适用于大内核大小的情况。

无论您是否读完我写的所有内容,我的问题是:

如何使用CUDA对相对较大的图像和非常小的kernel(3x3)进行快速的二维卷积?


4
“the CPU is still a lot faster”是什么意思?你是计时整个程序包括将内存复制到GPU和从GPU复制内存的时间,还是只计算内核启动和完成所需的时间? - Brendan Wood
我现在不需要计时,我可以看到使用CPU的程序完成得快得多 :( - paulAl
1个回答

9
你说得对,3x3的卷积核不适合基于FFT的方法。最好的处理方式是将卷积核推入常量内存(如果你在使用Fermi+卡,则这不会太重要)。
由于你知道卷积核的大小,最快的方法是将输入图像/信号的块读入共享内存,并执行展开的乘加操作。

--

如果你愿意使用库来执行此操作,ArrayFireOpenCV 都有高度优化的卷积例程,可以节省大量开发时间。
我不太熟悉 OpenCV,但在 ArrayFire 中,你可以这样做。
array kernel = array(3, 3, h_kernel, afHost); // Transfer the kernel to gpu
array image  = array(w, h, h_image , afHost); // Transfer the image  to gpu
array result = convolve2(image, kernel);       // Performs 2D convolution

编辑

使用ArrayFire的额外好处是其批量操作允许您并行执行卷积。您可以在这里阅读有关如何将卷积支持批处理操作的信息。

例如,如果您有10个图像想要使用相同的内核进行卷积,您可以执行以下操作:

array kernel = array(3, 3, h_kernel, afHost);     // Transfer the kernel to gpu
array images = array(w, h, 10, h_images, afHost); // Transfer the images to gpu
array res    = convolve2(images, kernel); // Perform all operations simultaneously

--

全面披露:我在AccelerEyes工作,积极参与ArrayFire的开发。


这些链接已经失效了。更令人沮丧的是,它们在Wayback Machine归档中被明确清除了:http://www.accelereyes.com/robots.txt - Hjulle
@Hjulle 我们从Accelereyes重新品牌为Arrayfire。对我而言,链接会重定向到我们当前的文档。如果您有问题,我很抱歉。我已更新代码和链接以反映Arrayfire的最新版本。 - Pavan Yalamanchili
如果我听起来有些烦躁,对不起。不过OpenCV的链接仍然无法打开。 - Hjulle
1
@Hjulle,我似乎找不到gpu :: Convolve的直接链接,但我已经链接到了讨论卷积的图像处理页面。 - Pavan Yalamanchili

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接