モバイルヘテロジニアスコンピューティングテクノロジー-GPUOpenCLプログラミング(高度)

写真

はじめに:この記事では、OpenCLに関する深い知識を説明すると同時に、モバイル端末の主流GPUメーカーの1つであるQualcomm Adrenoのチップ設計と組み合わせて、OpenCLプログラミングを最適化するための一般的な方法を説明します。モバイル端末。全文は5201語で、推定読書時間は14分です。


I.はじめに

モバイルヘテロジニアスコンピューティング技術-GPUOpenCLプログラミング(基本)」では、モバイルGPUコンピューティングの現状とOpenCLプログラミングの基本概念を紹介します。この記事では、OpenCLに関する深い知識をさらに詳しく説明すると同時に、モバイル端末の主流GPUメーカーの1つであるQualcomm Adrenoのチップ設計と組み合わせて、OpenCLプログラミングを最適化するための一般的な方法を説明します。モバイル端末。
備考:Qualcomm GPUシリーズに加えて、Huawei Kirin、MediaTek Tianji、およびその他のチップは、ARMによって設計されたMaliシリーズGPUを使用します。この記事では、スペースの制限のため、個別に説明しません。


2.基本的な概念

OpenCL

OpenCLは、異種システムでのクロスプラットフォーム並列プログラミングのためにKhronos組織によって開発および保守されている、オープンで無料の標準です。この設計は、開発者が最新の異種システムを使用して強力な並列コンピューティング機能を発揮すると同時に、ある程度のクロスプラットフォームを実現するのに役立ちます。

OpenCLクアルコム

クアルコムは、モバイル端末でOpenCLコンピューティング機能を完全にサポートする最初のチップメーカーの1つであり、国内および国際市場で一定の市場シェアを占めています。


3.OpenCL構造

写真

抽象OpenCLアプリケーションには通常、次の部分が含まれています。

  1. CPUホスト:OpenCLアプリケーション全体のマネージャーおよびスケジューラーとして、OpenCL全体の実行プロセスを制御します。

  2. OpenCLデバイス:GPU、DSP、FPGAなどの特定のOpenCLハードウェアデバイス。

  3. OpenCLカーネル:タスクを実行するOpenCLカーネルコードは、OpenCLホストによってコンパイルされ、対応するハードウェアで実行されます。

モバイルデバイス用のOpenCL

目前的经验来看,Android阵营中的移动端设备,OpenCL通常使用GPU作为硬件加速端。高通的建议是在移动端选择GPU作为OpenCL的加速设备(注:其实多数情况并无选择可能,有且只能获得到一个GPU的device)。


四、OpenCL兼容性

程序可移植性

OpenCL提供了不错的程序兼容性,一套OpenCL的代码,在不同的设备上都可以正常运行。当然,少部分基于硬件拓展能力,取决于当前硬件的支持情况。

性能可移植性

与程序兼容性不同,OpenCL的性能可移植性通常是比较差的。作为一种高级的计算标准,OpenCL硬件部分的实现是依赖厂商的,每个厂商都会有各自的优点和缺点。因此,针对不同的硬件平台,如高通Adreno 或者Arm Mali,同样的代码的性能表现是不同的。即使是相同的产商,随着硬件的迭代,相应的驱动也会有对应的微调,以充分利用新一代硬件的全部能力。针对不同的设备或者硬件针对性优化是非常必要的。当然这个是一个ROI(投入回报)问题。

向后兼容性

OpenCL的设计尽可能的保证向后兼容性。如果要使用已经过时的能力的话,只需要引入特定的头文件即可。值得注意的是:OpenCL的拓展是不完全向后兼容的,这些拓展通常由硬件厂商结合硬件特性来提供,因此应用时需要考虑到不同硬件之间的拓展兼容性。


五、高通 Adreno OpenCL架构

写真

图中为高通Adreno GPU OpenCL(Adreno A5x GPUS)上层架构,OpenCL在执行过程中涉及到几个关键的硬件模块。

Shader (or streaming) processor (SP) (着色器、流处理器)

  • Adreno GPU的核心模块,包含众多硬件模块,如算数逻辑单元、加载存储单元、控制流单元、寄存器文件等。

  • 运行图形着色器(如顶点着色器、片元着色器、计算着色器等),运行计算负载,如OpenCL内核等。

  • 每个SP对应一个或多个OpenCL的运算单元。

  • Adreno GPU可能包含一个或者多个SP,取决于芯片的档次,上图中展示的是单个SP的情况。

  • SP加载和读取Buffer类型或者带有__read_write标记的Image类型数据对象时,可以利用L2缓存。

  • SP加载只读的Image类型的数据对象时,可以利用L1缓存或者纹理处理器。

Texture Processer (TP) (纹理处理器)

  • 根据内核的调度来进行纹理操作,如纹理的读取、过滤等。

  • TP和L1缓存相结合,减少从L2缓存中读取数据时的缓存丢失几率。

Unified L2 Cache (UCHE) (统一L2缓存)

  • 响应SP对于Buffer类型的读取和加载,以及L1对于Image类型的数据的加载操作。


六、如何编写高性能OpenCL代码

性能兼容性

前文提及了OpenCL的性能兼容性,由于不同硬件的特性并不相同,因此在一块芯片上的调优后的OpenCL代码在另一块芯片上性能可能并非最优的。需要参考对应硬件的文档来进行特异性的优化工作。对于不同的芯片,针对性的优化是必要的。

手段总览

OpenCL程序的优化通常可以分为以下三类:

  • 程序、算法级别优化

  • API级别优化

  • OpenCL内核优化

程序算法以及API层级的优化手段是较为通用的,此处主要展开OpenCL内核的优化手段。

OpenCL的优化问题本质上一个如何利用内核带宽和计算能力的问题。即合理的利用全局内存、本地内存、寄存器、多级缓存等,以及合理的利用逻辑运算单元、纹理单元等等。

程序是否适用OpenCL

开发者需要确定程序是否适合使用OpenCL编写,可以通过以下几个方面来判断:

  • 是否存在较大的数据输入

  • 程序本身是否是计算密集型

  • 程序是否对并行计算亲和

  • 程序中的控制流操作相对较少

将CPU代码改造为GPU代码时性能Tips

明确了上述的几个关键点之后,开发者可以着手将CPU的代码转化为OpenCL的代码,为了达到一个最优的性能,需要关注以下几个方面:

  • 一些情况下,将多个CPU的操作合并到一个OpenCL内核当中可以得到性能收益。这个方式通常适用于减少GPU和主存之间的内存拷贝。

  • 一些情况下,将一个复杂的CPU程序拆分成几个简单的OpenCL内核,可以得到更好的程序并行性,进而达到全局性能最优。

  • 开发者需要考虑重新设计整体的数据架构,便于减少数据传递的开销。

这些情况要结合实际的情况进行考量,通常也是高性能异构编程本身的难点所在。

并行化CPU和GPU的工作流

充分的利用芯片的计算性能,应当合理的规划任务,在GPU执行一些计算工作的同时,CPU也可以同时承担部分工作。通常可以总结为以下几点:

  • 使CPU去执行CPU善于执行的部分,比如分支控制逻辑,以及一些串行的操作。

  • 尽可能避免GPU进入闲置状态,等待CPU下达进一步任务的情况。

  • CPU和GPU之间的数据传递成本极高,为了减少这部分成本,可以将一些本身适合CPU进行的任务放到GPU进行。


七、性能分析

性能Profile

可以结合Profile手段来分析程序性能。由于OpenCL程序分为宿主的CPU的调度逻辑,以及GPU硬件上的执行逻辑。开发者可以分别从CPU调度流程以及GPU执行两个层面去进行性能的Profile。通常CPU Profile是用来衡量整个流程端到端的性能,GPU Profile用来衡量OpenCL内核性能

CPU Profile

可以采用标准的c++编程方式,例如通过 gettimeofday 之类的api去进行CPU流程间的时间统计。
本文中列出部分示例代码,详细demo可参考OpenCL Profile(github.com/xiebaiyuan/…

#include <time.h>
#include <sys/time.h>
void main() {
    struct timeval start, end;
    // get the start time
    gettimeofday(&start, NULL); 
    // execute function of interest
    {
        . . .
        clFinish(commandQ);
    }
    // get the end time
    gettimeofday(&end, NULL); 
    // Print the total execution time
    double elapsed_time = (end.tv_sec - start.tv_sec) * 1000. +       \
                (end.tv_usec - start.tv_usec) / 1000.;
    printf("cpu all cost %f ms \n", elapsed_time);
复制代码

GPU Profile

OpenCL提供了对GPU Kernel Profile的API,分别获取OpenCL任务的各个环节的时间节点,便于开发者进行性能优化。

// opencl init codes 
...
// cl gpu time profile
cl_event timing_event;
cl_ulong t_queued, t_submit, t_start, t_end;
// add event when clEnqueueNDRangeKernel
int status = clEnqueueNDRangeKernel(runtime.queue, runtime.kernel, 1, nullptr, &ARRAY_SIZE,
nullptr, 0, nullptr, &timing_event);
check_status(status, "clEnqueueNDRangeKernel failed");
clWaitForEvents(1, &timing_event);
clGetEventProfilingInfo(timing_event, CL_PROFILING_COMMAND_QUEUED,
sizeof(cl_ulong), &t_queued, nullptr);
clGetEventProfilingInfo(timing_event, CL_PROFILING_COMMAND_SUBMIT,
sizeof(cl_ulong), &t_submit, nullptr);
clGetEventProfilingInfo(timing_event, CL_PROFILING_COMMAND_START,
sizeof(cl_ulong), &t_start, nullptr);
clGetEventProfilingInfo(timing_event, CL_PROFILING_COMMAND_END,
sizeof(cl_ulong), &t_end, nullptr);
printf("t_queued at %llu  \n"
"t_start at %llu  \n"
"t_submit at %llu  \n"
"t_end at %llu  \n"
"kernel execute cost %f ns \n"
"", t_queued, t_start, t_submit, t_end, (t_end - t_start) * 1e-0);
复制代码

通过上述的api可以得到OpenCL Kernel从进去队列,提交、开始、结束的各个时间点,并且可以计算出Kernel运算时长:

t_queued at 683318895157  
t_start at 683318906619  
t_submit at 683318897475  
t_end at 683318907168  
kernel execute cost 549.000000 ns
复制代码
‍丨性能瓶颈
复制代码

识别和定位整个程序的性能瓶颈是非常重要的,没有找到性能的瓶颈,即使其他的环节性能得到优化,也无法使得整个应用性能得到提升。

瓶颈定位

对于OpenCL内核,瓶颈通常是内存瓶颈与计算瓶颈二者之一。
这里提供两个简单的方式,稍微修改代码即可验证:

  • 加入额外的计算逻辑,如何没有影响性能,那应当不是计算瓶颈。

  • 反之,加入更多的数据加载逻辑,如何没有影响性能,那应当不是数据瓶颈。

解决性能瓶颈

成功的定位到性能瓶颈之后,有一系列的手段可以去针对性的解决:

  • 如果是计算瓶颈,可以尝试一些降低计算复杂度的方式、减少计算数的方式,或者使用 OpenCL提供的 fase relax math 或者 native math 等。在精度不高的时候可以使用fp16替代fp32进行计算。

  • 如果是内存瓶颈,可以尝试去优化内存的访问策略,如使用向量化的内存加载和存储,利用本地内存或者纹理内存等。在可能的情况下使用更短的数据类型,可以有效的降低内存带宽。


八、总结

この記事では、Qualcomm Adreno GPUを例として使用して、OpenCLの設計アイデアをさらに詳しく説明すると同時に、OpenCL高性能プログラミングの一般的な方法論について説明します。スペースに限りがあるため、より詳細なコンテンツは十分に拡張されておらず、この方向に関心のある小さなパートナーは、「BaiduGeekSaid」の公式アカウントに引き続き注意を払うことができます。

9.参考文献

[1]OpenCLガイド

github.com/KhronosGrou…

[2]OpenCL-例

github.com/rsnemmen/Op…

[3]マリ-GPU

en.wikipedia.org/wiki/Mali\_…

[4] Adreno-GPU

en.wikipedia.org/wiki/Adreno

推奨読書:

大規模なC++コンパイルパフォーマンス最適化システムであるOMAXの概要

Baiduのスマートミニプログラム検査およびスケジューリングソリューションの進化

モバイルヘテロジニアスコンピューティングテクノロジー-GPUOpenCLプログラミング(基本)

クラウドネイティブは開発とテストを強化します

おすすめ

転載: juejin.im/post/7104179560429649928