OpenCL内存性能优化 (2)

46 篇文章 21 订阅

7.1.4 全局内存

OpenCL应用程序可以使用两种类型的全局内存对象,即缓冲区和映像,并且都使用片外系统RAM。 与缓冲区对象(它是存储在系统RAM中的简单一维数据数组)相比,在开发人员无法假定内部数据存储方式的布局或格式的意义上,图像对象是不透明的存储对象。 创建图像对象后,该软件会以某些方式排列数据,以使GPU能够更高效地进行访问。 使用它们的最佳方法大不相同,以下各节将进行讨论。

7.1.4.1 缓冲区

缓冲区对象存储元素的一维集合,这些元素可以是标量数据类型(例如整数,浮点数),矢量数据类型或用户定义的结构。 使用以下API函数创建一个缓冲区对象:

cl_mem clCreateBuffer (cl_context context,
cl_mem_flags flags,
size_t size,
void *host_ptr,
cl_int *errcode_ret)

缓冲区对象存储在全局内存中,并通过Adreno GPU中的L2缓存进行访问。 在此函数中,最重要的参数是cl_mem_flags。 OpenCL为此功能提供了许多不同的标志,而如何选择和组合这些标志对于性能而言非常重要。 这里有几点:

  • 某些标志可能会导致额外的内存副本。 尝试使用第7.4节中讨论的零复制标志。
  • 某些标志适用于具有专用GPU内存的台式机/离散GPU。
  • 使用最准确的标志。 一般的想法是,随着标志的限制越来越严格,OpenCL驱动程序可以为对象找到更好的配置并提高其性能。 例如,它可以强加最适合内存对象的缓存刷新策略(直写,回写等)。 第7.4.2节详细介绍了缓存策略及其对性能的影响。 这里有一些例子:
  1. 如果内存是主机只读的,则使用CL_MEM_HOST_READ_ONLY。
  2. 如果内存不能被主机访问,则使用CL_MEM_HOST_NO_ACCESS。
  3. 如果内存仅用于主机写入,请使用CL_MEM_HOST_WRITE_ONLY。

7.1.4.2 图片

图像对象用于存储一维,二维或三维纹理,帧缓冲区或图像数据,并且图像对象内部的数据布局是不透明的。 实际上,对象中的内容不必与实际图像数据相关联。 任何数据都可以存储为图像对象,以利用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具有与上一节中讨论的缓冲区对象相似的经验法则。

Adreno GPU支持多种图像格式和数据类型。 从Adreno A3x GPU到Adreno A5x GPU,添加了新的图像格式和数据类型对。 用户可以使用功能clGetSupportedImageFormats来获取支持的图像格式/数据类型的完整列表。

为了充分利用内存带宽,建议使用长度为128位的对,例如CL_RGBA / CL_FlOAT,CL_RGBA / CL_SIGNED_INT32等。

7.1.4.3 使用图像对象与缓冲区对象

使用图像对象而不是缓冲区对象具有以下优点:

  • 利用纹理引擎硬件。
  • 利用L1缓存
  • 内置图像边界处理。
  • 支持第7.1.4节“图像”下列出的多种图像格式和数据类型组合,并支持自动格式转换。

OpenCL支持两个采样器过滤器,CLK_FILTER_NEAREST和CLK_FILTER_LINEAR。 对于CLK_FILTER_LINEAR,图像类型的正确组合允许GPU使用其内置的纹理引擎进行自动双线性插值。

例如,假定图像的类型为CLK_NORMALIZED_COORDS_TRUE和CL_UNORM_INT16,即图像日期为2字节无符号短。 函数调用read_imagef将执行以下操作:

  • 从图像对象读取像素(然后将其缓存在L1缓存中)。
  • 插值硬件中的相邻像素。
  • 将其转换并归一化为[0,1]的范围

这对于双线性或三线性插值运算很方便。

有时,缓冲区对象可能是更好的选择:

  • 更灵活的数据访问:
  1. 图像对象仅允许访问像素大小边界,例如,对于RGBA和128位/通道图像对象,访问权限为128位。
  2. 对于缓冲区对象,Adreno支持字节可寻址访问。 例如,如果没有超出缓冲区边界,则可以从缓冲区对象中的任何字节地址加载128位数据。
  • 如果L1成为瓶颈。
  1. 例如,存在严重的L1缓存抖动,这使L1缓存访问效率低下。

可以在内核内部读写缓冲区对象。 尽管还可以从OpenCL 2.0读取和写入图像对象,但是由于同步要求,其性能通常较差。

在这里插入图片描述

7.1.4.4 图像和缓冲区对象的使用

代替仅使用纹理对象或仅使用缓冲区对象,更好的方法是充分利用UCHE<=>SP和UCHE<=>TPL1<=>SP路径。 由于TPL1具有L1高速缓存,因此最好将L1存储为最常用但相对较少的数据。

7.1.4.5 全局内存与本地内存

本地内存的一种使用情况是,首先将数据加载到本地内存中,进行同步以确保数据准备就绪,然后工作组中的工作项可以将其用于处理。 但是,由于以下原因,使用全局内存可能比LM更好。

  • 可能具有更好的二级缓存命中率和更好的性能
  • 代码比本地内存更简单,并且工作组更大

7.2 最佳内存加载/存储

在前面的部分中,我们讨论了有关如何使用不同类型的内存的一般指导。 在本节中,我们将介绍一些关键的和一般的要点,这些要点对于内存加载/存储的性能至关重要。

7.2.1合并的内存加载/存储

合并的加载/存储是指结合来自多个相邻工作项的加载/存储请求的功能,如第3.3.1节中所述,用于本地内存访问。 合并访问对于全局内存加载/存储也很重要。

合并存储以类似于读取的方式工作,除了负载是2向过程(请求和响应),而存储是1向过程。 因此,合并的负载通常比存储更重要。

自Adreno A4x GPU以来,在Adreno GPU中,逐渐启用了硬件合并访问,如表7-3所示。 专用内存不支持合并访问。
在这里插入图片描述

7.2.2 矢量化加载/存储

矢量化加载/存储是指对单个工作项以矢量化方式进行的多个数据加载/存储。 这与合并访问不同,合并访问是针对多个工作项的。 以下是使用向量化加载/存储的一些关键点:

  • 对于每个工作项,建议以多个字节的块(例如64位/ 128位)加载数据,这样可以更好地利用带宽。
  1. 例如,可以将多个8位数据手动打包到一个元素(例如64位/ 128位)中,该元素使用vloadn加载,然后使用as_typeN函数(例如as_char16)解压缩。
  2. 请参见第9.2.3节中的向量化操作示例。
  • 为了获得最佳的SP到L2带宽性能,加载/存储内存地址应为32位对齐。
  • 有两种方法可以进行向量化的加载/存储:
  1. 使用内置函数(vload / vstoren),该函数在OpenCL中已定义好。
  2. 或者,可以使用指针强制转换进行向量化的加载/存储,如下所示:
char *p1; char4 vec;
vec = *(char4 *)(p1 + offset);
  • 建议使用最多包含4个组件的矢量化加载/存储指令。 具有超过4个组成部分的数据类型的矢量化加载将被分为多个加载/存储指令,每个指令占用不超过4个组成部分。
  • 避免在一个工作项中加载太多数据。
  1. 加载太多数据可能会导致寄存器占用量增加,从而导致工作组规模变小并影响性能。 在最坏的情况下,这可能会导致寄存器溢出,即编译器必须使用系统RAM来存储变量。
注意:
矢量化的ALU计算也可以提高性能,尽管通常不如矢量化的内存加载/存储计算那样。

7.2.3 最佳数据类型

数据类型很重要,因为它不仅影响内存流量,还影响ALU操作。 以下是有关数据类型的一些规则:

  • 检查应用程序管道的每个阶段中的数据类型,并确保在整个管道中使用的数据类型是一致的。
  • 如果可能,请使用较短的数据类型,以减少内存获取/带宽,并增加可用于执行的ALU数量。

7.2.4 16位浮点(半)与32位浮点

强烈建议使用一半数据类型而不是浮动数据类型,因为Adreno GPU具有专用的硬件来加速一半数据类型的计算。 半个ALU的触发器几乎是完整ALU的两倍。 以下是一些规则:

  • 16位半数的精度支持有限。 它只能精确表示狭窄范围内的数据。
  1. 例如,它只能在整数值上准确表示[0,2048]。
  • 如果一半数据计算导致不可接受的精度损失,则将一半转换为浮点数以进行计算。 但是将结果存储为一半数据类型。

7.3 原子功能

OpenCL中定义了一组局部和全局原子函数,而Adreno GPU本身在硬件中支持所有这些原子函数。 以下是使用原子函数时的一些规则:

  • 避免由单个或多个工作组中的工作项频繁更新单个全局原子内存地址,因为原子操作是序列化操作,并且其性能通常不如并行操作好。
  • 尝试首先使用本地原子,并自动对全局内存进行单个更新。
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值