我正在开发一个处理大图像的 OpenCL 1.2 应用程序。 目前,我正在测试的图像是16507x21244像素。我的内核在一个循环中运行,该循环对图像块进行操作。内核需要32bpp(rgba)的图像块,然后传递float4像素块。
让我们将(正方形)块的一侧(像素)定义为块大小。也就是说, 8192x8192像素正方形的块大小为8192 。当然,在右侧和底侧,如果图像不能被块大小干净地分割,则我们有较小的矩形块。我的代码处理了这个,但是对于本文的其余部分,让我们为了简单起见忽略它。
我试图确定我可以在循环的每次迭代中操作的最大块大小,以及最佳块大小(可能不是最大块大小)。 / p>
供参考,here is the information reported by the clinfo utility on my machine。我正在Geforce GTX 560 Ti
使用他们专有的Linux驱动程序在Nvidia平台上运行我的内核。
我最初的天真假设是我可以使用最大2d图像大小。
但是,这会导致clEnqueueNDRangeKernel
返回错误代码-4(CL_MEM_OBJECT_ALLOCATION_FAILURE
)。
考虑一下,这对我来说很有意义。凭借1 GiB的视频内存,人们可以期望能够容纳单个16384x16384像素纹理(32bpp)或8192x8192像素纹理(float4)。如果在内核运行时都需要在卡上缓存,我们可以使用以下内存:
4 bytes-per-pixel * chunk size^2 (input image)
+ 16 bytes-per-pixel * chunk size^2 (output image)
= 1 GiB total video memory
解决我们得到的块大小
chunk size = sqrt(1GiB/20)
插入OpenCL报告的内存量(略低于1GiB - 1023 MiB)并对结果进行分层,我们得到:
floor(sqrt(1072889856/20)) = 7324
但是,块大小为7324仍会导致CL_MEM_OBJECT_ALLOCATION_FAILURE
。
我的下一个猜测是我们无法传递大于最大分配大小的图像,OpenCL报告为我的卡的268222464字节。因为我的输出图像具有更大的像素宽度,所以它将决定我的块大小。
floor(sqrt(268222464/16)) = 4094
嘿,这实际上有效! 现在如果我们试图变大呢?令我惊讶的是,它没有失败。通过反复试验,我将6784缩小为实际的最大块大小。在6785,它开始抱怨CL_MEM_OBJECT_ALLOCATION_FAILURE
。我不知道为什么max似乎是6784,我不知道这是否可重复或者值是否波动(例如视频内存中存在的其他状态会影响它能保持多少。)我也发现运行时块大小为6784比基于最大分配的大小运行慢几秒。我想知道这是否是因为OpenCL需要在引擎盖下执行多个(昂贵的)分配? CL_DEVICE_MAX_PARAMETER_SIZE
)。但是,这个价值似乎是假的。如果我只能传入4096个字节,则会将我限制为16x16像素!所以我留下了两个基本问题:
作为一个额外的问题,是否有任何良好资源我可以针对低级OpenCL硬件交互来解决此类性质的未来问题?
最后,我将提供一些代码片段供同行评审;我会非常感谢任何建设性的批评!
一如既往,提前感谢您的帮助!
答案 0 :(得分:2)
回答您的直接问题:
1)要确定单个内核操作可以使用的绝对最大块大小,必须知道块大小'是参考。例如,OpenCL内存结构中有五个已定义的内存模型。其中一个是主机内存,我们将忽略它。其他四个是全球的,不变的,本地的和私人的。
要获得有关硬件支持的任何信息,我强烈建议您访问底部记录的Khronos API文档。您可以收集大量有关您的设备的元数据。例如,存在设备可以支持的2D和/或3D图像的最大高度和最大宽度的查询。我还建议您查看CL_DEVICE_LOCAL_MEM_SIZE和CL_DEVICE_MAX_COMPUTE_UNITS来定义您的工作组。甚至还有一个允许的CL_DEVICE_MAX_MEM_ALLOC_SIZE查询。
指出您对性能的关注是为您提供的内存大小是工作组或项目的最佳最大大小(取决于查询)。可能发生的事情是将记忆泄漏到全球空间。这需要跨不同工作人员进行更多内存分配,从而导致性能下降。该声明并非100%肯定,但当您超出推荐的缓冲区大小时,它可能是问题的一部分。
2)要确定最快的块大小,不需要尝试和错误。在书中" OpenCL编程指南" Addison-Wesley发布了一个关于在宿主应用程序中使用事件进行性能分析的部分。有许多功能可以被分析。这些功能如下:
要启用此分析,一旦创建了队列,就需要设置标志CL_QUEUE_PROFILING_ENABLE。然后是函数clGetEventProfilingInfo(cl_event事件,cl_profiling_info param_name,size_t param_value_size,void * param_value,size_t * param_value_size_ret);可用于提取时序数据。然后,您可以让主机应用程序根据需要处理此数据,例如:
通过使用此分析信息,您可以确定最快的块大小'通过您的软件或分析,然后使用常量为整个板块大小。
奖金问题一些好的资源将是" OpenCL编程指南"由Addison Wesley出版,由Aaftab Munshi,Benedict R. Gaster,Timothy G. Mattson,James Fung和Dan Ginsburg撰写。我还想说Khronos docs有很多信息。
作为旁注您正在主机代码中的双嵌套循环内运行此内核...这种打破了使用并行编程的全部原因。特别是在图像上。我建议重构代码并研究GPU操作的并行编程模型。还要做一些关于在OpenCL中设置和使用内存屏障的研究。英特尔和Nvidia就此有一些很好的论文和例子。 Finally, the API docs are always available