对齐图像卷积(OpenCL/CUDA)内核的GPU内存访问

gpu*_*guy 12 cuda gpgpu memory-alignment opencl gpu-programming

为了理解如何确保对齐要求得到满足,我多次阅读以下文章,从OpenCL p.no:157中的异构计算一书.这显示了如何为图像卷积中的问题填充填充(假设工作组大小为16 x 16).

对齐记忆访问

NVIDIA和AMD GPU的性能均受益于全局内存中的数据对齐.特别是对于NVIDIA,在128字节边界上对齐访问和访问128字节段将理想地映射到存储器硬件.但是,在这个例子中,16宽工作组将只访问64字节段,因此数据应该对齐到64字节的地址.这意味着每个工作组访问的第一列应该从64字节对齐的地址开始.在此示例中,使边框像素不生成值的选择确定所有工作组的偏移量将是工作组维度的倍数(即,对于16 x 16工作组,工作组将开始访问列N*16处的数据) .为确保每个工作组正确对齐,

1 - 任何人都可以帮助我理解填充每个工作组访问的第一列后是如何从64字节对齐的地址开始的(上述段落中提到的要求,对吧?)?

2 - 该图也是正确的声明:对于16 x 16工作组,工作组将开始访问N*16列的数据.

如果正确,图中所示的工作组1,2应该开始访问第1x16列的数据,这与图中所示的相反.我完全糊涂了!! :(

更新: Q-2现在对我来说很清楚. 实际上图中显示的工作组是2,1(在opencl惯例中,第一列),所以它完全正确:2x16 = 32而不是我想的1x16.

但问题不是.1仍然没有答案.

在此输入图像描述

Rob*_*lla 7

对于卷积核,每个区域(例如区域(0,1)或区域(2,1)等)也必须包括其周围的数据"晕",以便当卷积内核在数据元素上运行时在该区域的边缘,它具有该数据元素的一组合适的邻居,以计算该数据点处的卷积.这意味着对于在其左上角有数据元素(0,0)的区域(0,0),我需要元素(-1,0),( - 2,0)等来计算卷积在元素(0,0).

现在,如果我正常存储图像,那么元素(0,0)在内存位置0(或其他一些64字节对齐的地址),那么当我访问该点之前的元素进行卷积时,我将是访问我的64字节对齐区域之外的数据.因此,我们可以使用"向左"的附加数据元素"填充"图像的最左列,即先前的地址,以便卷积内核获取全部在64字节对齐区域内的值,而我不是跨越64字节边界.我们不是在存储器位置0开始图像存储,而是在存储器位置零处开始光晕存储,并且第一图像数据元素从位置0 +光晕宽度开始.这种填充还可以具有对齐其他区域的光晕边界的效果,

现在让我们假设图像是一些非2的宽度(例如,高清图像的1920像素宽).如果我们只是将光晕宽度作为填充包含在图像的右侧(即在像素行的末尾),然后我们在紧随其后的下一个像素行的光晕区域开始,我们将也不太可能在下一个像素行(包括光环)处开始正确对齐的区域.因此,我们在每行的末尾添加额外的填充(任何卷积操作都不会触及它,这只是浪费的空间),这样当我们开始下一个像素行的光晕区域时,它就会从正确对齐的地址开始.

这个讨论和方法(我相信这个问题)实际上只关注确保每个工作组数据访问的起始地址是一致的.只要第一个工作组的起始地址对齐(通过适当的填充和调整存储器中的图像存储),并且工作组具有适当的尺寸(例如16宽,每个工作4个字节),那么起始地址下一个工作组也将保持一致.当然,相邻工作组的数据访问之间会有重叠,因为相邻工作组的光晕区域是重叠的.

我在这里使用它的对齐有一个相当简单的定义.如果地址的最低有效n位全为零,则存储器中的地址为2**n字节对齐.因此,64字节对齐的区域具有起始地址,其中6个最不重要的位全部为零.这通常对这些体系结构有用,以满足体系结构的存储器负载和存储要求,特别是它们包含的DRAM子系统.现代DRAM存储库访问总是返回多个字节,因此如果我们在代码中的同一位置同时使用所有这些字节,我们可以最有效地使用传输.有关对齐的更多内容及其对数据访问的合并和改进的影响,您可能对此网络研讨会感兴趣(和幻灯片)来自nvidia网络研讨会页面.为了快速浏览,本演示文稿的第26-33页幻灯片涵盖了基本概念.