OpenCL 通用编程与优化(2)
3 骁龙上的OpenCL
骁龙是当今安卓操作系统和物联网(IOT)市场上领先的移动平台之一。骁龙移动平台在单个芯片上汇集了一流的移动组件,确保基于骁龙的设备以极其节能、集成的解决方案提供最新的移动用户体验。
Snapdragon是一个多处理器系统,包括多模式调制解调器、CPU、GPU、DSP、位置/GPS、多媒体、电源管理、射频、软件和操作系统的优化、内存、连接(Wi-Fi、蓝牙)等组件。
要了解当前包含骁龙处理器的商用设备列表,并了解更多关于骁龙处理器的信息,请访问http://www.qualcomm.com/snapdragon/devices。骁龙处理器中的Adrenogpu通常用于渲染图形应用程序,它也是一种功能强大的通用处理器,能够处理许多计算密集型的任务,如图像和视频处理,以及计算机视觉。
3.1 骁龙OpenCL
Adrenogpu从A3xgpu开始就完全接受了OpenCL。随着OpenCL随着不同版本和配置文件的发展,对Adrenogpu上的OpenCL的支持也在不断发展。表3-1显示了在Adrenogpu上可用的OpenCL版本和配置文件。
表3-1Adrenogpu及其对OpenCL支持
GPU series | Adreno A3x | Adreno A4x | Adreno A5x | Adreno A6x | Adreno A7x |
---|---|---|---|---|---|
OpenCL version | 1.1 | 1.2 | 2.0 | 2.0 | 3.0 |
OpenCL profile | Embedded | Full | Full | Full | Full |
在相同的OpenCL版本或配置文件中,一些特性和功能可能在Adreno GPU家族中有所不同,例如扩展的可用性、图像对象的最大尺寸、图像格式等。属性的详细列表可以通过相关的OpenCL API函数查询,如clGet数据数据信息或cl数据平台信息。
3.2 Adreno GPU架构
本节提供了与OpenCL相关的Adreno架构的高级概述。
3.2.1 Adreno高级架构的OpenCL
图3-1针对OpenCL的Adrenogpu的高级架构
Adrenogpu支持一组丰富的图形和计算api,包括OpenGL ES、OpenCL、DirectX和Vulkan等。图3-1说明了针对OpenCL的Adreno硬件架构的高级视图,其中跳过了仅支持图形的硬件模块。每一代Adrenogpu之间都有很多差异,而对于OpenCL,差异很小。OpenCL执行的关键硬件模块如下:
- 着色器(或流式)处理器(SP)
- Adrenogpu的核心块。包含关键的硬件模块,如运算逻辑单元(ALU)、加载/存储单元、控制流单元、注册文件等。
- 它执行图形着色器(例如,顶点着色器、片段着色器和计算着色器)和计算工作负载,如OpenCL内核。
- 每个SP对应于OpenCL中的一个或多个OpenCL计算单元,因此,它执行一个或多个工作组。
- AdrenoGPU可能包含一个或多个SPs,这取决于GPU系列和层。一个低层芯片组可能有一个SP,而一个高或高级层芯片组可能有更多的SP。在图3-1中,有两个SPs。
- SPs通过级别2(L2)缓存加载和存储由__read_write限定符(OpenCL 2.0+特性)定义的缓冲区对象和图像对象的数据。
- SPs从纹理处理器/L1模块为只读图像对象加载数据。
- 纹理处理器(TP)和L1高速缓存
- L1高速缓存为只读状态。
- TP从L1缓存或L1缓存丢失的L2缓存获取数据。
- TP根据内核的请求执行纹理操作,如纹理获取和过滤。
- 第二层快取存储器
- 支持读写。
- 处理以下请求:
- 缓冲区数据从sp加载/存储。
- 图像数据存储。
- 从L1高速缓存中加载图像数据。 - SP和TP只是L2高速缓存的许多客户端中的两个。
3.2.2 波和纤维
在Adrenogpu中,最小的执行单元被称为光纤。一根光纤对应于OpenCL中的一个工作项。总是同步执行的纤维集合称为波。以下是波和纤维的一些特性:
-
SP和波:
SP一次可以容纳多个活动波。
SP可以同时在一个或多个波上执行ALU指令。
每波可以独立前进,不管其他波的状态如何。
在较新的Adrenogpu中,SP可以执行属于不同工作组的波。
A波对应于OpenCL中的一个子群 -
光纤的数量和波的大小:
波的大小取决于Adreno GPU系列和层以及编译器;值可以为8、16、32、64、128等。这可以通过一个API函数(例如,sub_group_size())来查询。
Adreno图形处理器支持两种模式,全波模式和半波模式。详见第9.2.1节。
一旦编译,内核的GPU给定的波大小是固定的。 -
波的数量和工作组的大小:
工作组中流水线化的最大波数与硬件和内核有关。
- 对于一个给定的GPU,总寄存器是固定的。内核需要的寄存器越多,允许的最大波就越少。
一个工作组可能有一个或多个波,这取决于工作组的大小。–例如,如果工作组大小小于或等于波大小,则一个波就足够了。
对于给定的内核,最大工作组大小是最大允许的波数和波大小的乘积。–一般来说,内核越复杂,它所需要的寄存器就越多。
OpenCL1。x不公开波的概念,而OpenCL 2.0及更高版本允许应用程序通过名为cl_khr_subgroups的扩展使用波。自OpenCL 2.1以来,许多子组函数已经成为其核心特性。OpenCL 3.0通过KHR扩展为子组引入了一组新的缩减和洗牌函数。此外,Adrenogpu通过供应商扩展支持一系列其他子组/洗牌函数。详见第9.2节。
3.2.3 延迟隐藏
延迟隐藏是GPU实现高效并行处理的最强大的特性之一,并使GPU能够实现高吞吐量。这里是一个例子:
- SP开始执行第一波。
- 在几条ALU指令之后,这一波需要来自外部内存(可能是全局/本地/专用内存)的额外数据来继续进行,但这是不可用的。
- SP发送请求来获取此波的数据。
- SP将执行切换到准备执行的下一个波。
- SP继续执行下一波程序,直到依赖关系尚未准备就绪。如果第一波的数据可用,
- SP可以切换到下一波或返回到第一波。
这样,SP主要是繁忙的,并且“全职”工作,因为延迟,或者依赖关系可以很好地隐藏。如果内核没有足够的波来隐藏延迟,那么它可以限制延迟。
3.2.4 2级(L2)缓存
L2高速缓存是系统内存和sp之间的一个基本块。以下是理解它如何工作的关键点:
- 当工作项从系统内存中读取一个字节时,内存系统不仅加载该字节,还加载包含该字节的整个缓存行。缓存行可以被GPU硬件驱逐,以存储新的缓存行。
- 当一个工作项将一个字节写入系统内存时,内存系统需要加载缓存行,修改它,然后在某个点将其写出来。
- 在驱逐缓存行之前,内核应该尽可能多地使用缓存行中的数据。
- 缓存大小和缓存行大小可能因不同的GPU代和层而不同,尽管大多数AdrenoGPU的缓存行大小为64字节。
- 开发人员可以通过clGetDeviceInfo API函数查询缓存配置的列表。其中包括以下信息:
- 缓存类型(只读或读写、CL_DEVICE_GLOBAL_MEM_CACHE_TYPE)。
- 高速缓存行的大小,单位为字节(CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE,例如,64字节)。
- 缓存字节大小(CL_DEVICE_GLOBAL_MEM_CACHE_SIZE,例如,256KB)。
- 开发人员可以通过将缓存大小除以缓存线大小(例如,4096条缓存线,缓存大小为256KB,缓存线为64字节大小)来获得GPU中可用的缓存行数量。
- 开发人员可以通过clGetDeviceInfo API函数查询缓存配置的列表。其中包括以下信息:
- 缓存刷新,即在重用之前必须清除一些缓存行,如果L2缓存过载,可能会严重影响性能。
- 基于缓存的大小,开发人员可能会减少或限制工作组的工作负载,以避免太快填充缓存线,并提高缓存的局部性。
3.2.5 工作组分配
一个典型的OpenCL内核会启动多个工作组。Adrenogpu将每个工作组分配给一个SP,并且每个SP同时处理一个或多个工作组。如果有,其余的工作组将排队等待sp执行。多个SP无法处理一个工作组。
在早期的Adrenogpu中,一个SP一次只能处理一个工作组,并且一个工作组必须完成执行,另一个工作组才能在SP上启动。Adreno A6x和A7x的高级层已经取消了这些限制,并完全支持每个SP的并发工作组执行。
以图3-2中的2D范围为例,假设这是一个带有4个sp的GPU。图3-3显示了不同的SPs如何处理工作组。在本例中,有9个工作组。假设没有并发工作组在运行,那么每个工作组都由一个SP执行。每个工作组有四个波,波的大小为16个。
图3-2在Adrenogpu中的工作组布局和调度示例
图3-3工作组分配给SPs的示例
OpenCL标准既没有定义工作组启动/执行的顺序,也没有定义工作组同步的方法。对于Adrenogpu,开发人员不能假定sp中的工作组或波的启动顺序。
3.2.6 合并访问
协同访问是OpenCL和GPU并行计算中的一个重要概念。基本上,它指的是底层硬件可以通过多个工作项将数据加载/存储请求合并到一个请求中,以提高数据加载/存储效率。在没有合并访问支持的情况下,硬件必须按照每个单独的请求执行加载/存储操作,从而导致过多的请求和性能损失。
图3-4说明了合并数据负载与非合并数据负载之间的差异。为了组合来自多个工作项的请求,数据的地址通常需要是连续的,或者在一定的地址范围内(例如,128位范围)。在合并的情况下,Adrenogpu可以在一个事务中加载四个工作项的数据,而如果不合并,它将需要相同数据量的四个事务。
图3-4合并数据负荷与非合并数据负荷的说明