OpenCL 通用编程与优化(13)
8.8 通用内存地址空间和已命名内存地址空间
自OpenCL 2.0以来,已经引入了一个名为通用内存地址空间的特性。在OpenCL 2.0之前,指针必须指定其内存地址空间,如本地、私有或全局空间。此特性允许指针不在内核中设置其地址空间。GPU在内核执行过程中确定真实的地址空间。该特性使开发人员能够重用和减少代码库,这对于库开发等任务特别有用。
由于与识别内存空间相关联的硬件成本,使用通用内存地址空间可能会导致性能损失。以下是一些关于内存地址空间的提示:
- 开发人员应该明确指定内存地址空间。这将减少编译器的模糊性,并避免GPU硬件成本来识别真实的内存空间。
- 尽量避免使用发散的内存地址空间。对于统一的情况,编译器可能能够提取内存空间,并避免让硬件识别其内存空间。
- 准备具有不同内存地址空间的不同版本。
8.9 子组
OpenCL 2.0中的新子组函数提供了比工作组对工作项的更细的粒度控制。一个工作组有一个或多个子组,而Adrenogpu中的子组被精确地映射到波的概念上。与1D/2D/3D工作组相比,子组只有一个维度。与工作组类似,一组函数允许工作项查询其本地ID和子组中的其他参数。
子组的强大功能在于,OpenCL引入了一组丰富的函数,允许子组中的工作项共享数据,并跨子组中的工作项执行各种操作。如果没有该特性,跨工作项的数据共享可能不得不依赖于本地或全局内存,这通常是昂贵的。
它是由硬件供应商来选择如何实现子组功能。它可以通过硬件或软件模拟来加速。在Adrenogpu中,许多子组功能都是硬件加速的。
除了核心OpenCL中的子组函数外,在OpenCL 3.0中还有子组上的KHR扩展。在使用扩展之前,必须先检查它们的可用性。
子组功能大致有两种类型:减少和洗牌。
- 还原: Adreno对还原功能有硬件支持,比通过本地内存进行还原要快得多。
- 洗牌:洗牌允许将数据从一个工作项传递到另一个工作项。它通常支持上洗牌、向下洗牌和通用洗牌。
除了支持标准的子组函数外,Adrenogpu还支持通过供应商扩展进行的子组减少和洗牌。详见第9.2.2节。
8.10 联合使用
虽然联合是OpenCL内核语言中的一个标准特性,但在Adrenogpu上是低效的。编译器需要分配额外的寄存器来处理不同大小的成员;因此,性能通常比没有并集的常规内核要差。开发人员应该避免在Adrenogpu上使用联合使用。
8.11 结构的使用
结构可以使代码更容易理解和组织,这是将一组相关变量分组到一个地方的极好方式。尽管有优点,但在Adrenogpu上使用结构可能会导致一些效率,并不总是推荐。以下是一些关于结构化的提示:
- 会尽量避免使用结构体内部的指针。
- 显式地分配单个成员,而不是将整个结构变量分配给另一个成员。
- 选择数组结构或结构组数组。
- 一个关键的考虑因素是,该选择是否可以缓解内核的瓶颈。
- 例如,如果可以安排数组,使来自内存的数据加载有更好的合并,那么数组的结构是一个更好的选择。如果结构体中的成员导致良好的缓存本地性,那么结构体的
- 数组可能是一个更好的选择。
该决策将取决于每个用例的特征。
8.12 其他
许多其他一些看起来很小的优化技巧都可以提高内核的性能。以下是开发人员可以尝试一下的一些事情:
-
预先计算在内核内不会更改的值。
- 计算一个可以在内核之外预先计算的值是一种浪费。
- 预计算值可以通过内核参数或#depal传递给内核。
- 使用快速整数内置函数。使用mul24用于24位整数乘法,使用mad24用于24位整数乘法和累积。
- Adrenogpu有对mul24的本机硬件支持,而32位整数乘法需要不止一条指令。
- 如果整数在24位范围内,则使用mul24比直接的32位乘法更快。
-
减少EFU功能。
- 例如,代码如下:
r = a / select(c, d, b<T)
其中a、b和T是浮点变量,c和d是常数,可以重写为:
r = a * select(1/c, 1/d, b<T)
这避免了倒数的EFU函数,因为编译器可以在编译时推导出1/c和1/d。
- 例如,代码如下:
-
避免除法操作,特别是整数除法。
- 整数划分在Adrenogpu中非常昂贵。
- 与其使用分割,不如使用naten_recep进行互操作,如8.3节所述。
-
避免整数模块操作,这是昂贵的Adrenogpu。
-
对于常数组,如查找表、过滤器水龙头等,在内核作用域之外声明它们。
-
使用mem_fence函数来拆分/组代码部分。
- 编译器有复杂的算法来从全局优化的角度生成最优代码。
- mem_fence可能会防止编译器在代码前后进行洗牌/混合代码。
- mem_fence允许开发人员操作一些代码来进行分析和调试。
-
使用16位的ALU计算,而不是8位的。8位可能必须转换为16位或32位的ALU操作,因为Adrenogpu没有一般的8位ALU支持。
-
如果可能,使用位移位操作而不是乘法。