AI学习者 · 2021年11月15日

华为诺亚方舟实验室 Bolt v1.2.1 在 QualComm GPU 上的优化总结

Bolt 是基于华为诺亚方舟实验室研究成果,开源社区孵化的高性能深度学习推理加速库,目前已更新至v1.2.1版本,新版本增加了对QualComm GPU的深度优化

image.png

图0 Bolt v1.2.1版本主要更新 feature

本文从 QualComm GPU 面向深度学习计算优化现状出发分析当前环境下第三方自主优化面临的挑战介绍Bolt自研的一套移动端GPU系统性调优机制

下面图1 为 Bolt 在 Adreno 660 / 650 运行目标分类、分割等一系列模型的性能表现,其中ghostnet/ocr_detect/solov2/ssd为内部模型。比较对象隶属业界开源框架第一梯队,因笔者时间关系只测了一家,就匿名为FrameWorkA。

image.png

图1. Bolt QualComm GPU FP16 Test

背景与现状

QualComm GPU 即高通骁龙芯片搭载的 Adreno 系列的 GPU ,目前最新的骁龙芯片为 SnapDragon 888,搭载 Adreno 660 GPU,根据维基百科(https://en.wikipedia.org/wiki... )给出的半精度浮点数计算峰值可达 3244 Gflops/s。测试 FrameWorkA 在 Adreno 660 GPU卷积计算的性能表现,如图2所示,

image.png
图2. FrameWorkA Adreno 660 Conv Test

因测试样本的局限性,上述结果可能无法准确的反应业界优化的最高水平,但相对比较也可以得出以下结论:

使用 OpenCL/OpenGL/Vulkan (NeuralTalk评:看到这里懂的都懂)三种不同的GPU编程框架,优化水平基本一致,OpenCL取得了明显的性能优势;即使是性能最高的OpenCL,对卷积这样的计算密集型算子,性能依然未超过200 Gflops/s,相对官方给出的性能峰值差距巨大;另一方面,QualComm官方在今年5月份针对机器学习计算推出了OpenCL ML SDK(https://developer.qualcomm.co...)。其中提到,从Adreno 660 GPU开始可用的内部扩展指令clqcommlops对算子性能有明显提升。图3为官方给出的算子列表,在adreno 660实测卷积算子(1 64 72 72)(64 64 3 3)(1 1)计算耗时0.23ms,获得了1.5 Tflops/s的性能。但OpenCL ML SDK没有为用户提供可编程接口或者clqcommlops的使用路径,目前仅能调用SDK提供的算子,无法进行自定义计算

QualComm 官方对 Adreno GPU OpenCL 优化提供的最新文档停留在 2017 年(https://developer.qualcomm.com/qfile/33472/80-nb295-11_a.pdf),即针对Adreno 5xx/4xx的优化方法,针对Kernel级别的优化可总结为以下几点:

  • GPU 内存尽量使用具备L1 cache的Image,且满足合并读取向量化要求;
  • 使用寄存器或Local Memory暂存数据减少对global memory的访问次数提升计算内存访问比
  • Kernel内避免线程分支与线程同步,尝试循环展开;
  • 使用半精度浮点数代替全精度,减少数据读写量;
  • 使用暴力搜索寻找对当前 Kernel 最优的 localworksize;

image.png

图3. clqcomml_ops受益算子列表

以上几点均为 GPU 性能优化的常用方法,相信资深从业人员也以此为基准进行过反复的迭代优化,但实际效果却差强人意。总结当前QualComm GPU深度学习优化现状,最新的QualComm GPU硬件本身在深度学习计算中可获得超Tflops/s的计算性能,但在当前开发环境下,用户难以通过自主开发享用该性能优势。QualComm GPU对深度学习计算的支持比较封闭,即提供不可用户编程的SDK或者专用硬件,总之业界相关从业人员很难受。

Bolt GPU优化方法

在当前背景下,Bolt通过一套自研的GPU系统性调优机制获得了相对较高的计算性能。图4为Adreno 660 Bolt卷积计算性能,在测试的卷积计算中,最高性能接近600Gflops/s,而最低性能也超过300Gflops/s
image.png

图4. Bolt Adreno 660 Conv Test针对Adreno GPU优化支持,Bolt GPU系统性调优机制实现了以下几点功能:

  1. Image内存自适应机制:对计算密集型算子的input/output Tensor尽可能使用image存储,以便利用L1 cache获得更高性能。同时自适应判断是否可与其他Tensor复用以减少内存占用;是否满足当前设备的image size limited,如果不满足也能灵活的替换为Buffer,保证计算的顺利进行;
  2. 基于编译机制的计算Kernel自动生成:通过宏参数与宏函数定义Kernel代码自动生成模板,基于上层实时传递的计算配置信息,例如每个线程负责计算的块大小、Input/Output Tensor类型以及算子融合类别等,自动生成对应宏参数并编译得到具体的Kernel(NeuralTalk评:有《CLBlast》那个味道了
  3. Kernel计算方案与线程配置自动调优:在前两点功能的扶持下,Bolt可基于当前算子的实时信息快速生成可能的计算方案,配合算法选择与localworksize选择,在目标硬件上获取性能最佳的方案,且提供了preprocess_ocl工具将调优过程离线化并打包调优结果与相关Kernel Binary信息为动态库

图 5 为调优整体流程,首先 image 内存自适应机制根据当前计算硬件以及Tensor size将使用的memory类型传递给infer forward algorithm。其次infer forward algorithm会基于算子参数尝试所有可能的计算方案,找到最佳的计算配置。最后由gcl run kernel selectls确定当前Kernel最佳的localworksize。
image.png

图5. Bolt GPU调优机制

上述过程中,生成Kernel编译宏参数即可编译出对应的计算Kernel,实则是因为Bolt在Kernel层面定义了多套Kernel生成模板

下面讲个例子,以卷积模块”convdirectsh1_qc.cl“https://github.com/huawei-noa...为例,该模块可生层stride=1&dilation=1时不同数据复用方案的计算Kernel,对卷积核fw没有限制,fh方向进行了循环展开,目前可支持的最大值为12。input/output/weight的数据排列方式与《Bolt GPU性能优化,让上帝帮忙掷骰子》文中描述一致,均采用了C4的数据格式。

图6描述了该卷积模块的具体实现, KN为单线程output channel方向计算点数,ON为单线程height方向计算点数,单线程计算总点数即为:

image.png
FH方向第一个点计算访问比为:
image.png

后续计算复用inval已存的数据,仅需从全局内存更新一个点 :
image.png
 计算访存比为,搜索空间包括:

image.png

上层传递具体参数后inferforward_algorithm在以上空间中搜索最佳计算配置。
image.png

图6. convdirectsh1_qc模块实现

总结

移动端 GPU 作用于深度学习计算本身还有大量计算潜力有待挖掘,但仍然面临着很多挑战,希望可以早日看到更加统一高效透明的编程框架,也希望整个行业生态能越来越好。

文章转载于:NeuralTalk
作者:HUAWEI

推荐阅读

推荐阅读
关注数
18839
内容数
1375
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息