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

12

为了确保满足对齐要求,我多次阅读了《Heterogeneous Computing with OpenCL》第157页的以下段落。这展示了如何为图像卷积中的一个问题设置填充(假设16 x 16工作组大小)。

内存访问对齐

NVIDIA和AMD GPU上的性能都受益于全局内存中的数据对齐。特别是对于NVIDIA,将访问对齐在128字节边界上,并访问128字节段将理想地映射到内存硬件。然而,在此示例中,16宽的工作组仅将访问64字节段,因此数据应该对齐到64字节地址。这意味着每个工作组访问的第一列应该从64字节对齐地址开始。在此示例中,选择使边框像素不产生值决定所有工作组的偏移量将是工作组维度的倍数(即,对于16 x 16工作组,工作组将从列N * 16开始访问数据)。 为确保每个工作组正确对齐,则唯一的要求是使用额外的列填充输入数据,以便其宽度成为工作组的X维度的倍数。

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

2-关于该图,语句是否正确:对于16 x 16工作组,工作组将从列N * 16开始访问数据。

如果是正确的,则图中显示的工作组1,2应该从第1列x16开始访问数据,这与图中所示的相反。 我完全困惑了!! :(

更新: 问题2现在对我来说已经清楚了。 实际上,在OpenCL约定中,图中显示的工作组是2,1(先按列),因此它是完全正确的:2x16 = 32而不是我所想的1x16。

但是问题1仍未得到解答。

enter image description here

1个回答

8
对于卷积核来说,每个区域(例如区域(0,1)或区域(2,1)等)还必须包括围绕它的一圈数据“光环”,以便当卷积核在处理该区域边缘的数据元素时,它有一个合适的邻居集来计算该数据点的卷积。这意味着对于区域(0,0),它的左上角有数据元素(0,0),我需要元素(-1,0)、(-2,0)等来计算元素(0,0)处的卷积。
现在,如果我正常存储图像,使得元素(0,0)位于内存位置0(或其他64字节对齐的地址),那么当我访问卷积之前的元素时,我将访问64字节对齐区域外的数据。因此,我们可以在图像的最左列“左侧”即地址之前添加额外的数据元素,以便卷积核选择所有在64字节对齐区域内的值,并且我不会跨越64字节边界。我们从内存位置0开始存储光环,并且第一个图像数据元素从0 + 光环宽度的位置开始。假设区域的x和y维度是工作组x和y维度的倍数,如图所示,此填充还可以对齐其他区域的光环边界,如红色虚线的交点所示。
现在假设图像具有某些非2次幂宽度(例如高清图像的1920个像素宽)。如果我们只是在图像右侧(即像素行末尾)包括光环宽度作为填充,然后我们立即开始下一个像素行的光环区域,我们也不太可能有一个适当对齐的区域从下一个像素行开始(包括光环)。因此,我们在每行末尾添加额外的填充(不会被任何卷积操作触及,它只是浪费空间),以便当我们开始下一个像素行的光环区域时,它始于适当对齐的地址。
这个讨论和方法(以及问题,我相信)实际上只关注确保每个工作组数据访问的“起始地址”对齐。只要第一个工作组的起始地址通过适当的填充和调整内存中的图像存储对齐(例如16个宽,每个工人4个字节),并且工作组具有适当的维度,则下一个工作组的起始地址也将对齐。当然,相邻工作组的数据访问将重叠,因为相邻工作组的光环区域是重叠的。

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


谢谢您的回复。64字节边界是什么,64字节对齐又是什么呢? - gpuguy
为了大家的阅读体验,请将一些段落编辑到这个答案中! - talonmies
我已经编辑了答案,添加了段落格式,并回答了@gpuguy的问题。 - Robert Crovella
@RobertCrovella 很好的解释!那正是我想要理解的。 - gpuguy

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