OpenCL 通用编程与优化(8)
7.1.4私有内存
在OpenCL中,私有内存对每个工作项都是私有的,其他工作项无法访问。在物理上,私有内存可以驻留在片上寄存器或片外系统内存中。确切的位置取决于几个因素,这里是一些典型的案例:
- 标量变量存储在寄存器中,它比其他内存更快。
- 如果寄存器不足,私有变量将保存在系统内存中。
- 私有数组可以存储在:
- 本地内存中,但不能保证这样做。
- 芯片外系统内存,如果它们超过本地内存容量。
将私有内存存储到芯片外系统内存中是非常不可取的,原因有二:
- 系统内存的延迟时间远高于GPR。
- 私有内存访问模式对缓存不友好,特别是当每个工作项的私有内存数量很大时。
建议:
- 避免在内核中定义任何私有数组。如果可能的话,尽量使用向量。
- 用全局或本地数组替换私有数组,并设计其布局,以便对数组元素的访问可以跨多个相邻的工作项进行合并。缓存性能可以更好。
- 使用矢量化的私有内存加载/存储,即,尝试加载/存储128位,使用vload4/vstore4每次加载4个32位元素。
7.1.5全局内存
OpenCL支持使用芯片外系统RAM的缓冲区和图像对象。与存储在系统RAM中的简单一维数据数组的缓冲区对象相比,图像对象是一个不透明的内存对象,其中开发人员不知道底层数据是如何存储的。当创建一个图像对象时,软件以特定的方式排列数据,以便GPU有效地访问。使用它们的最佳方法是不同的,并将在下面的章节中进行讨论。
7.1.5.1缓冲区对象
缓冲区对象存储元素的一维集合:标量数据类型、向量数据类型或用户定义的结构。缓冲区对象的内容由内核通过Adrenogpu中的L2缓存加载或写入。使用以下API函数创建一个缓冲区对象:
cl_mem clCreateBuffer(cl_context context
cl_mem_flags flags,
size_t size,
void *host_ptr,
cl_int *errcode_ret)
在这个函数中,cl_mem_flags是开发人员必须小心使用的一个重要标志,因为它可能会显著影响性能。OpenCL允许对这个功能有许多不同的标志,对于Adrenogpu,这里有几个关键点:
- 某些标志可能会产生额外的内存副本。尝试使用第7.4节中描述的零拷贝标志。
- 有些标志是用于具有专用GPU内存的桌面/离散GPU的。
- 使用最准确的标志:
- 一般的想法是,标志越严格,OpenCL软件就越有可能找到对象的最佳配置。
- 例如,OpenCL软件可以应用缓存刷新策略(通写、回写等)。最适合内存对象,缓存刷新的开销可以忽略。
- 第7.4.2节详细介绍了高速缓存策略及其对性能的影响。以下是以下几个例子:
–如果内存是主机只读的,则使用CL_MEM_HOST_READ_ONLY。
–如果内存无法访问,请使用CL_MEM_HOST_NO_ACCESS。
–如果内存仅供主机写入,请使用CL_MEM_HOST_WRITE_ONLY。
7.1.5.2图像对象
图像对象存储1D、2D或3D纹理、帧缓冲区或图像数据,并且图像对象内部的数据布局是不透明的。在实践中,对象中的内容不必与实际的图像数据相关联。任何数据都可以存储为一个图像对象,以利用硬件纹理引擎及其在Adreno中的L1缓存。使用以下API创建了一个图像对象:
cl_mem clCreateImage(cl_context context,
cl_mem_flags flags,
const cl_image_format *image_format,
const cl_image_desc *image_desc,
void *host_ptr,
cl_int *errcode_ret)
请注意,图像的cl_mem_flags与上一节中讨论的缓冲区对象有类似的经验法则。
Adrenogpu支持许多图像格式和数据类型。经过几代代,增加了更多的图像格式和数据类型对。开发人员可以使用clGetSupportedImageFormats函数来获得设备上可用的图像格式/数据类型的完整列表。为了充分利用内存带宽,开发人员应该使用长度为128位的内存对,例如,CL_RGBA/CL_FlOAT、CL_RGBA/CL_SIGNED_INT32等。
Adrenogpu还通过供应商扩展支持非OpenCL标准的格式,如YUV和压缩格式。除了新的格式,许多新功能还为图像对象的硬件加速,如盒子过滤、SAD和SSD。详情请参见第9章。
7.1.5.3图像对象与缓冲区对象
如第6.2节所述,Adrenogpu处理图像对象比缓冲对象更好,因为它有各种优点,包括强大的纹理引擎、专用的L1缓存和自动处理边界外访问。Adrenogpu支持许多图像格式和数据类型组合,并可以进行自动格式转换。OpenCL支持两个采样器过滤器,CLK_FILTER_NEAREST和CLK_FILTER_LINEAR。对于CLK_FILTER_LINEAR,适当的图像类型的组合允许GPU使用其内置的纹理引擎进行自动的双线性/三线性插值。例如,假设一个图像是CLK_NORMALIZED_COORDS_TRUE和CL_UNORM_INT16,即,图像数据是2字节的无符号短。要执行read_imagef,Adrenogpu需要执行以下操作:
- 通过L1高速缓存读取图像对象中的像素。
- 使用所有需要的像素来执行插值。
- 将其转换并规范化到[0,1]的范围。
这对于双线性/三线性插值操作很方便。Adrenogpu还通过供应商扩展支持双立方插值。有关更多细节,请参考第9.3.4节,但有时,缓冲区对象可能是一个更好的选择:
-
缓冲区允许更灵活的数据访问:
-
图像对象只允许在像素大小边界处进行访问,例如,128位的RGBA和32位/通道的图像对象。
-
Adreno支持对缓冲区对象的灵活访问,其中指针为数据访问提供了极好的灵活性。
-
L1高速缓存成为瓶颈。
- 例如,存在严重的L1缓存抖动,这使得L1缓存访问效率低下。
-
缓冲区对象允许在内核内部进行读取和写。OpenCL 2.0支持内核中的读写图像(具有__read_write限定符的图像对象)。由于同步的要求,读写映像的性能可能比一些老式Adrenogpu上的缓冲区要差。
表7-2Adrenogpu中的缓冲区与图像
Features | Buffer | Image |
---|---|---|
L2 cache | Yes | Yes |
L1 cache | Unavailable on most GPUs | Yes |
Support read-and-write in kernel | Yes | No in OpenCL 1.x Yes, in OpenCL 2.x (synchronization required) |
Use of pointer | Yes | No |
Built-in hardware interpolation | No | Yes |
Built-in boundary handling | No | Yes |
Support image format/sampler | No | Yes |
7.1.5.4同时使用图像对象和缓冲区对象
更好的方法是同时充分利用L2缓存SP和L2缓存TPL1SP路径,而不是只使用纹理或缓冲区对象。由于TPL1具有L1高速缓存,所以让L1存储最常用但相对较少的数据量是一种较好的做法。
7.1.5.5全局内存与本地内存的比较
本地内存的一个典型用例是首先将数据加载到本地内存中,同步以确保数据准备就绪,然后工作组中的工作项可以使用它进行处理。但是,由于以下原因,使用全局内存可能优于本地内存:
- 本地内存的一个典型用例是首先将数据加载到本地内存中,
- 同步以确保数据准备就绪,然后工作组中的工作项可以使用它进行处理。但是,由于以下原因,使用全局内存可能优于本地内存