首发:NeuralTalk
作者:腾讯优图
这次TNN又迎来重大更新,下面一起看看吧!
TNN由腾讯优图实验室打造,移动端高性能、轻量级推理框架,同时拥有跨平台、高性能、模型压缩、代码裁剪等众多突出优势。TNN框架在原有Rapidnet、ncnn框架的基础上进一步加强了移动端设备的支持以及性能优化,同时也借鉴了业界主流开源框架高性能和良好拓展性的优点。目前TNN已经在手Q、微视、P图等应用中落地,欢迎大家参与协同共建,促进TNN推理框架进一步完善。
01
Why-FP16指令加速背景
1.1 什么是FP16数据?FP16指令有什么好处?
FP16是半精度浮点格式,相比常用的FP32单精度浮点,数据宽度降低了一半。2016年Arm更新了Armv8.2-A Extension扩展指令集,其中包含FP16半精度浮点运算。Arm NEON向量指令长度为128位,一条FP32向量可完成4个单精度浮点数运算,一条FP16向量可完成8个半精度浮点数运算,使理论峰值性能翻倍。如果该指令用于加速网络推理,相比于FP32预期能达到2倍加速。
1.2 为什么要支持Arm32位FP16指令加速?
智能手机分为Arm32和Arm64位两种架构,其中Arm64占绝大比例,苹果从2013年9月发布iPhone5s后,所有机型全都是Arm64架构。在Arm64架构手机上,App编译为64位可以获得最大的性能,但Arm64架构也支持按照32位编译的APP运行,而Arm32架构无法支持按照64位编译的APP运行。因此出于机型覆盖率以及软件包大小的考虑,当前众多App只会发布32位编译的版本。
经调研,行业开源推理框架如ncnn、MNN等仅支持Arm64位FP16指令加速,这样32位App无法享受FP16指令加速效果。针对这个行业缺失,TNN在架构兼容、模型兼容、代码结构设计等方面率先进行探索,对Arm64位和Arm32架构均实现了FP16指令优化,让64位和32位App都能发挥硬件FP16向量加速的能力。
02
How-如何支持FP16指令加速
2.1 架构兼容性设计
由于深度学习网络的算子种类繁多,并且随着新模型不断被开发,算子类型也会随之增加,因此,难以一次性为所有层提供FP16加速。我们采用TNN中广泛使用的注册机制,以实现FP16加速的增量开发。实现如下:
①在ArmDevice下维护一个全局的layer\_precision\_map,将算子类型映射到其支持的数据类型;
②每实现一个FP16加速算子,使用REGISTER\_ARM\_PRECISION\_FP16+LayerType,更新layer\_precision\_map;
③在运行模型的初始化过程,TNN根据layer\_precision\_map的信息,为模型中的每一层分发计算精度。仅当算子已支持FP16加速,并且运行平台具备FP16加速硬件时,该层才会使用FP16精度计算。当用户设置的网络精度为PRECISION\_HIGH时,可以强制禁用FP16加速。
2.2 模型兼容性设计
部署时模型的算子各种各种,为了获得最大的性能,TNN支持模型按照FP32和FP16混合运行加速。对模型中已实现FP16加速的算子,TNN默认自动按照FP16加速,而对模型中未实现FP16加速的算子,TNN在静态图中自动插入Reformat层转为FP32加速。如下图所示:
在TNN的图优化过程中,当发现Pad层不支持FP16加速时(如图a所示),会在其输入和输出分别插入Reformat层。Reformat层负责将FP16和FP32数据格式以及数据排布做相互转换,以支持Pad层单独采用FP32计算,其余层仍采用FP16计算。
如果模型中存在多个相连的层不支持FP16(如图b所示),TNN的图优化机制会避免在这些层之间插入成对的Reformat层,以提高运行效率。
2.3 代码结构设计
为了在32位和64位库中都支持FP16,整体代码结构如下图所示。其主要分为五个部分,通过CMake中不同的配置项,可编译出不同的target。各个部分的主要区别体现在针对不同Arm指令集实现了特定优化。
由上图可知,aarch32和aarch64 FP16指令代码独立于其他部分。因为编译FP16指令需要添加特定的编译选项,如果对TNN代码全局添加该选项,会导致编译器将选项应用到所有代码中,然后基于Armv8.2-A架构生成目标文件。
例如在Arm64 Target中,在编译Armv8指令代码时添加该选项,会生成一些Armv8.1或Armv8.2指令集中独有的指令。这些指令若在不支持v8.1和v8.2的Armv8 CPU上运行,会直接导致程序崩溃。因此,为了最大限度地提高兼容性,Armv8.2-A FP16指令代码被单独剥离,单独使用编译选项,避免影响其他部分。
综上所述,TNN库会同时包含两种架构的指令:
- 64位库:包含Armv8指令和aarch64 Armv8.2-A FP16指令。
- 32位库:包含Armv7指令和aarch32 Armv8.2-A FP16指令。
2.4 运行时兼容性设计
由上可知,TNN库中包含两种架构的指令。当运行64位或32位库时,若在不支持Armv8.2-A的CPU上执行Armv8.2-A指令,会直接导致程序崩溃,在运行时造成兼容性问题。
针对上述问题,在执行推理之前,TNN会判断当前运行的CPU是否在白名单中、是否支持Armv8.2-A。如果支持,则会运行FP16算子,否则仍然运行FP32算子,避免执行Armv8.2-A FP16指令。具体判断方式如下:
①在IOS和OSX下,通过系统调用sysctlbyname("hw.cpufamily"),获取CPU型号,然后与维护的白名单比较,判断CPU是否支持FP16加速。目前的白名单包括IOS的A11-A14,和OSX的M1。
②在Android和Linux下,通过系统调用getauxval(AT\_HWCAP),获取hwcap flag,然后与HWCAP\_FPHP和HWCAP\_ASIMDHP掩码比较,当FPHP位和ASIMDHP位都为1时,CPU可支持FP16加速。
由于Android的C库在API级别18及更高版本中才支持 getauxval,在低版本的32位Android系统中,该系统调用可能会失败。为了支持aarch32 FP16加速,采用直接解析/proc/cpuinfo的方式,判断CPU是否支持FP16加速。通过/proc/cpuinfo获取处理器的MIDR(Main ID Register),根据该标志可判断处理器厂商和型号,例如Arm的Cortex-A55,海思的Cortex-A76 (HiSilicon)等。然后与维护的白名单比较,最终判断硬件是否支持FP16加速。
03
What-FP16指令加速效果
TNN已对部分算子实现了FP16优化,在64位和32位库中均取得了不错的加速效果,相比于一些开源框架也具备一定的性能优势,如下图中的对比数据所示。
在A76大核上,TNN的FP32和FP16性能均能保持前列。在A55小核上,Bolt框架针对A55单独做了特殊优化,在性能测试时效果会更好。但实际应用中,由于进程可能会在小核和大核上动态调度,A55特殊优化版本在A76大核上运行的性能较差,所以实际应用的表现不一定会好。TNN采取的是相对折中的实现,在大核和小核上都能取得不错的性能表现。
04
Where-TNN如何启用FP16加速
TNN已经对大量的实现细节做了封装,只需要对少量参数进行配置,就可以轻松获得FP16的加速。
链接:https://github.com/Tencent/TNN
1)编译
TNN工程的根目录下提供了各个平台的编译脚本,只需要在这些脚本的基础上打开TNN\_ARM82\_ENABLE,就可以将Armv8.2的优化代码编译到TNN的lib中,当前默认是OFF。
2)运行
在初始化时,将precision参数设置成PRECISON\_AUTO,TNN内部就会根据CPU的型号以及layer的实现情况自动去调用FP16的代码,当前默认是PRECISION\_AUTO,所以不用做任何修改。
05
结束语
TNN当前正在与PCG光影团队合作,后续还会支持更多算子的Armv8.2-A FP16优化,同时也会尝试去实现Armv8.2-A的Dot扩展指令,优化在最新机型上的int8模型性能。
往期回顾
- 华为端引擎Bolt GPU性能优化:让上帝帮忙掷骰子
- 嵌入式AI简报2021-01-05: 骁龙480/微信AI超分/256KB内存检测
- 嵌入式AI简报2020-11-30: 华为Bolt发布1.0/ImgTech新AI加速器IP
本作品采用知识共享署名-相同方式共享 4.0 通用许可协议进行许可。
欢迎关注公众号,关注模型压缩、低比特量化、移动端推理加速优化、部署。
更多嵌入式AI相关技术干货请关注嵌入式AI专栏。