我已经了解了cuda中的块和网格的想法,我想知道是否有任何好的编写的辅助函数可以帮助我确定任何给定2D图像的最佳块和网格大小。
例如,对于this thread中提到的512x512图像。网格为64x64,块为8x8。
然而,有时我的输入图像可能不是2的幂,它可能是317x217或类似的东西。在这种情况下,网格可能是317x1,块应该是1x217。
因此,如果我有一个接受用户图像的应用程序,并使用cuda处理它,它如何自动确定块和网格的大小和尺寸,用户可以输入任意大小的图像。
是否存在处理此问题的辅助函数或类?
答案 0 :(得分:4)
通常,您希望根据GPU架构选择块的大小,目标是在流式多处理器(SM)上保持100%的占用率。例如,我学校的GPU可以运行每个SM 1536个线程,每个SM最多8个块,但每个块每个维度最多只能有1024个线程。因此,如果我要在GPU上启动1d内核,我可以用1024个线程最大化一个块,但是SM上只有1个块(占用率为66%)。如果我选择较小的数字,例如192个线程或每个块256个线程,那么我可以在SM上分别拥有6个和8个块的100%占用率。
要考虑的另一件事是必须访问的内存量与要完成的计算量。在许多成像应用中,您不仅需要单个像素的值,而且还需要周围的像素。 Cuda将其线程分组为warp,它同时逐步执行每条指令(目前,有32个线程可用于warp,但这可能会改变)。使块方块通常可以最小化需要加载的内存量与可以完成的计算量,从而提高GPU的效率。同样,由于Cuda一次加载内存行而不是单个值,因此2个2的幂的块更有效地加载内存(如果与内存地址正确对齐)。
因此,对于您的示例,即使将网格设置为317x1并且块为1x217似乎更有效,但如果在20x14的网格上启动16x16的块,则代码可能会更高效。将导致更好的计算/内存比率和SM占用率。但这确实意味着,在尝试访问内存之前,您必须在内核中检查以确保线程不在图片之外,例如
const int thread_id_x = blockIdx.x*blockDim.x+threadIdx.x;
const int thread_id_y = blockIdx.y*blockDim.y+threadIdx.y;
if(thread_id_x < pic_width && thread_id_y < pic_height)
{
//Do stuff
}
最后,您可以使用(N + M-1)/ M确定完全覆盖图像的每个网格维度中所需的最少块数,其中N是该维度中的总线程数,并且您有M个线程该维度中的每个区块。
答案 1 :(得分:0)
这取决于你如何处理图像。如果您的线程仅分别处理每个像素,例如,为每个像素值添加3,则可以只为块大小分配一个维度,为网格大小分配另一个维度(只是不要超出范围)。但是如果你想做像过滤器或侵蚀这样的东西,这种操作通常需要访问中心像素附近的像素,如3 * 3的9 * 9。然后该块应该是您提到的8 * 8,或其他一些值。你最好使用纹理记忆。因为当线程访问全局内存时,总会有32个线程在一个块中包装一次。
所以没有你描述的功能。线程和块的数量取决于您处理数据的方式,它不是通用的。