免责声明:这篇文章的所有内容都是我个人调研得到的,来源是网络的各种资料。难免出现各种错误,有的内容可能吸收了别人的工作成果。如果遇到问题,欢迎提出,我会及时更改。
API 和 ABI
这俩不是一个概念,API 是指 Application Program Interface,而 ABI 是指 Application Binary Interface。简单来说,API 和系统相关,ABI 和硬件相关。
API
打开一个 Android 项目,API 主要和 SDK Version 相关,项目主要有 三个 SDK Version,SDK 版本号和 API 级别对应,也和系统版本对应。
名称 | 含义 | 推荐取值 |
---|---|---|
compileSdkVersion | 编译时用到的 SDK 版本 | 最新的 SDK |
targetSdkVersion | 测试机的系统版本 | 测试机的系统版本 |
minSdkVersion | 前向兼容的系统版本 | 想兼容的版本 |
API Level 和 Android 系统的映射没有什么规律,有时映射大版本,有时映射小版本号。更多信息可以参考官方文档
Code name | Version | API level | Distribution% | AccuDist% |
---|---|---|---|---|
Android 10 | 10 | API level 29 | ||
Pie | 9 | API level 28 | 10.4 | 100.0 |
Oreo | 8.1.0 | API level 27 | 15.4 | 89.6 |
Oreo | 8.0.0 | API level 26 | 12.9 | 74.2 |
Nougat | 7.1 | API level 25 | 7.8 | 61.3 |
Nougat | 7.0 | API level 24 | 11.4 | 53.5 |
Marshmallow | 6.0 | API level 23 | 16.9 | 42.1 |
Lollipop | 5.1 | API level 22 | 11.5 | 25.2 |
Lollipop | 5.0 | API level 21 | 3.0 | 13.7 |
KitKat | 4.4 - 4.4.4 | API level 19 | 6.9 | 10.7 |
Jelly Bean | 4.3.x | API level 18 | 0.5 | 3.8 |
Jelly Bean | 4.2.x | API level 17 | 1.5 | 3.3 |
Jelly Bean | 4.1.x | API level 16 | 1.2 | 1.8 |
Ice Cream Sandwich | 4.0.3 - 4.0.4 | API level 15, NDK 8 | 0.3 | 0.6 |
Ice Cream Sandwich | 4.0.1 - 4.0.2 | API level 14, NDK 7 | 0.3 | 0.3 |
Honeycomb | 3.2.x | API level 13 | ||
Honeycomb | 3.1 | API level 12, NDK 6 | ||
Honeycomb | 3.0 | API level 11 | ||
Gingerbread | 2.3.3 - 2.3.7 | API level 10 | ||
Gingerbread | 2.3 - 2.3.2 | API level 9, NDK 5 | ||
Froyo | 2.2.x | API level 8, NDK 4 | ||
Eclair | 2.1 | API level 7, NDK 3 | ||
Eclair | 2.0.1 | API level 6 | ||
Eclair | 2.0 | API level 5 | ||
Donut | 1.6 | API level 4, NDK 2 | ||
Cupcake | 1.5 | API level 3, NDK 1 | ||
(no code name) | 1.1 | API level 2 | ||
(no code name) | 1.0 | API level 1 |
上表同时包含了各个 API Level 的 占比,最后一列是版本从低到高的累积占比,可以看到,陈旧系统能够的占比还是挺多的,比如 Android 6 之前的系统不保证 Neon 存在(Neon 后边会介绍),而占比达到了 25.2%,如果我们的代码只做了 Neon 优化,那么在 25.2% 的机器上无法运行。因为要考虑陈旧系统,所以 Android 的深度学习变得复杂。
ABI
在 Android 项目中,会看到 eabi 的字眼,例如有的教程里会说将某某类库放在 armeabi 文件夹下。ABI 全称是 Application Binary Interface,EABI 是指嵌入式的 ABI:Embedded Application Binary Interface
Android 支持的 ABI 如下,更详细信息可以参考 官网
flags | ||
---|---|---|
removed in NDK r17 | ||
removed in NDK r17 | ||
removed in NDK r17 | ||
armeabi-v7a | ||
https://android.googlesource.com/platform/ndk/+/ndk-r12-release/docs/HardFloatAbi.md | removed in NDK r12 | |
arm64-v8a | ||
x86 | ||
x86_64 |
Android 已经放弃支持
ARM CPU
我们常说的高通 855,麒麟980 不是 CPU 是 SoC(System On Chip),SoC 除了 CPU 外,还有 GPU,还有可选的浮点数加速器,专用于深度模型的加速器,这些都和本文相关。除此以外,SoC 还包括运存,基带芯片等等,这些和深度学习关系不大。
ARM 和各个 SoC 的关系:所有 ARM 的 CPU 都是 ARM 公司授权的,授权的形式是 IP 核,各个商场得到授权,生产自己的 SoC。
ARM 架构
历史上,ARM 定义了
在定义
-
ARMv7-A : Application -
ARMv7-R :Real-time -
ARMv7-M :Microcontroller
ARMv6-M 是后来出现的产品,由ARMv7-M 裁减得到。
在定义ARMv8 时,沿用了三个 profile,只有ARMv8-A 支持 64 位。ARMv8-A 迭代了多次,目前最新的是ARMv8.6-A
每个架构下,有多个 IP 核,目前 IP 核都以 Cortex 命名,例如Cortex-A5 是ARMv7-A 的一个 IP 核。
Android 仅支持ARMv7-A 和ARMv8-A 两种架构,对应的 EABI 是armeabi-v7a 和arm64-v8a
常见的 ARM 架构和 IP核 如下表
Architecture | bit-width | Cortex |
---|---|---|
ARMv7-A | 32 | A5, A7, A8, A9, A12, A15, A17 |
ARMv7-R | 32 | R4, R5, R7, R8 |
ARMv7-M | 32 | M3 |
ARMv7E-M | 32 | M4, M7 |
ARMv8-A | 32 | A32 |
ARMv8-A | 64 | A34 |
ARMv8-A | 32/64 | A35, A53, A57, A72, A73 |
ARMv8-R | 32 | R52 |
ARMv8-M | 32 | M23, M33 |
ARMv8.2-A | 32/64 | A55 |
ARMv8.2-A | 32/64 | A75 |
ARMv8.3-A | 64 | A76 |
高通公司的 SoC 对 ARM 的 IP 核做了二次包装,把 32位的 CPU 命名为 Krait 系列,把 64 位的 CPU 命名为 Kryo 系列。例如 骁龙855 使用的 CPU 是 Kryo 485,实际是由 Cortex-A55 + Cortex-A76 实现的。
Snapdragon | CPU | GPU | DSP |
---|---|---|---|
800 | Krait 400 | Adreno 330 | Hexagon QDSP6 V5 |
835 | Kryo 280 (A73) | Adreno 540 | Hexagon 682 |
845 | Kryo 385 (A55+A75) | Adreno 630 | Hexagon 685 |
855 | Kryo 485 (A55+A76) | Adreno 640 | Hexagon 690 |
855+ | Kryo 485 (A55+A76) | Adreno 640 | Hexagon 690 |
32位和64位
参考官网,未来的 app 都要支持 64bit,主要是 native code 要支持
- 自己开发的 native code 要支持
- 调用的第三方 native lib 也要支持
最终生成的 apk 里,同时有 32位 和 64位的内容
Platform | 32 bit | 64 bit |
---|---|---|
ARM | lib/armeabi-v7a/ | lib/arm64-v8a/ |
x86 | lib/x86/ | lib/x86_64/ |
如何设置 64位
在 gradle 中设置
1 2 3 4 5 6 7 8 9 10 | android { compileSdkVersion 27 defaultConfig { appId "com.google.example.64bit" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64' // ... |
在 cmake 中设置
1 2 | cmake -DANDROID_ABI=arm64-v8a ... cmake -DANDROID_ABI=x86_64 ... |
在 ndk-build 中设置
1 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 |
ARM 的浮点数运算
首先介绍 VFP(Vector Floating Point)和 Neon:两者都是浮点数运算器,在
对于
FPU | Function call | |
---|---|---|
yes | hard-float | |
armeabi-v7a | optional | soft-float |
arm64-v8a | yes | hard-float |
浮点数调用以及浮点数运算器总结如下,表格从上到下, 运算速度递增。
Type | Flag | Register, FPU | Android | 备注 |
---|---|---|---|---|
soft | Integer | 不使用 FPU | ||
softfp | Integer + VFP | 默认使用 vfpv3-d16 | ||
softfp-vfp | Integer + VFP | armv7a 默认 | 同 softfp | |
softfp-neon | Integer + Neon | armv7a 有 neon 时 | ||
hard | Float + VFP | armv7-a 无法用 | 默认使用 vfpv3-d16 | |
hard-neon | Float + Neon | armv8-a 默认 |
最简单的加速方法就是开启 Neon。
在 gradle 中开启,设置
1 2 3 4 5 6 7 8 9 | android { defautConfig { externalNativeBuild { cmake { arguments "-DANDROID_ARM_NEON=ON" } } } } |
在 cmake 中开启
1 | cmake -DANDROID_ARM_NEON=ON ... |
或者编译
1 2 3 4 5 6 7 | if(ANDROID_ABI STREQ ameabi-v7a) set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS -mfpu=neon) endif() # or if(ANDROID_ABI STREQ armeabi-v7a) set_source_files_properties(foo.cpp PROPERTIES COMPILE_FLAGS -mfpu=neon) endif() |
在 ndk 中开启
1 2 3 | LOCAL_ARM_NEON := true # or add .neon LOCLA_SRC_FILES := foo.c.neon bar.c |
因为
java 版本的检测示例:
1 2 3 4 5 6 7 8 9 10 11 12 | if (Library.isSupported(ArmCpuSimdFeature.NEON)) { if (Library.getMicroarchitecture().equals(CpuMicroarchitecture.Krait)) { /* Special NEON implementation for recent Qualcomm processors */ nativeClass.processKrait(); // v7 neon } else { /* Generic NEON implementation */ nativeClass.processNeon(); // v8 neon } } else { /* Generic implementation without NEON */ nativeClass.processGeneric(); // vfp } |
native 代码的检测示例
1 2 3 4 5 6 7 8 | #include <cpu-features.h> if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM && (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0) { // use Neon-optimized routines } else { // use non-Neon fallback routines instead } |
最后需要说明的是,
8512.pastedImage_5F00_46.png
ARMv7-A | ARMv8-A AArch32 | ARMv8-A AArch64 | |
---|---|---|---|
Register | 32x64-bit | 32x64-bit | 32x128-bit |
Integer | 8/16/32/64-bit | 8/16/32/64-bit | 8/16/32/64-bit |
Float | float | float | float/double |
Operation | 16x8-bit | 16x8-bit | 16x8-bit |
Instruction Set | 最少 | 比 ARMv7-A 多点 | 截然不同的 |
GPU
Android 主流的 GPU 只有两种,ARM 的 Mali 和高通的 Adreno。华为,MTK,三星都使用 Mali
Mali 是 ARM公司的,最早是 Falanx Microsystems 设计的,后边合并为 ARM 挪威公司。
GFLOPS | SoC | Date | |
---|---|---|---|
Mali-T830 | 40.8 | Kirin 650/655, Exynos 7870/7880 | 2015Q4 |
Mali-T860 | Helio P10 | 2015Q4 | |
Mali-T880 | 122.4 | Kirin 950/955, Helio P25/X20 | 2016Q2 |
Mali-G71 | 282 | Kirin 860, Helo P23/P30, Exynos 5 8782 | 2016Q2 |
Mali-G72 | 330 | Kirin 870, Helio P60/P70, Exynos 9 9810 | 2017Q2 |
Mali-G76 | 489.6 | Kirin 980/990, Helio G90/G90T, Exynos 9 9820 | 2018Q2 |
Adreno 是 ARM Radeon 字母换序所得,Adreon 100 系列是高通自研的,Adreon 200 和 AMD 合作的(Imageon),后来 ADM 把部门卖给了高通。
GFLOPS | SoC | Date | |
---|---|---|---|
Adreon 130 | 2007 | ||
Adreon 200 | 2.1 | Snapdragon S1 | 2008 |
Adreon 200 enhanced | 3.2/3.9 | Sanpdragon S1 | 2008 |
Adreno 203 | 7.8/9.4 | Snapdragon S4 Play | 2008 |
Adreno 205 | 7.8/8.5 | Snapdragon S2 | 2008 |
... | |||
Adreno 508 | 163.2 | Snapdragon 630 | 2017 |
Adreno 512 | 217.6 | Snapdragon 660 | 2017 |
Adreno 540 | 567 | Snapdragon 835 | 2017 |
Adreno 630 | 727 | Snapdragon 845 | 2018 |
Adreno 640 | 899/1037 | Snapdragon 855/855+ | 2019 |
Qualcomm Kryo 485 | 47.36 (2.96x16) | Snapdragon 855+ | |
Intel Haswell/Skylake | 83.2 (2.6*32) | Xeon E5-4627 V4 | |
TU102 | 13450 | Geforce RTX 2080 Ti |
最后三行分别是 高通CPU、PC端 CPU,以及 Nvidia GPU 的性能,可以有个直观的对比
Android 下的 GPU 没有好用的加速框架,一般使用 Vulkan,OpenGL ES
Vulkan | OpenGL ES | OpenCL | |
---|---|---|---|
Mali-T8XX | 1.0 | 3.2 | 1.2 |
Mali-GXX | 1.1 | 3.2 | 2.0 |
Adreno 5XX | 1.0 | 3.2 | 2.0 |
Adreno 6XX | 1.0, 1.1 | 3.2 | 2.0 |
Android 1.0+ | X | 1.0, 1.1 | no official |
Android 2.2+ | X | 2.0 | no official |
Android 4.3+ | X | 3.0 | no official |
Android 5.0-7 | X | 3.1 | no official |
Android 7-9 | 1.0 | 3.1 | no official |
Android 9+ | 1.1 | 3.1 | no official |
iOS | X | 3.0 | X |
iOS 12+ | X | deprecated | X |
版本范围都是左闭右开的。比较合理的加速方案是使用 OpenGL ES,适用范围最广。
专用于 AI 的加速方案
NNAPI
自 Android 8.1 后,Android 支持 NNAPI,对高通的支持较好。详情
各个厂商有自己的加速方案,然而没有统一的SDK,这给开发适配也带来了困难。
Huawei NPU
早期华为使用寒武纪开发的 NPU,目前华为的 NPU 是 Da Vinci 系列
NPU | GPU | |
---|---|---|
Kirin 990 5G | 2x Da Vinci Lite + 1x Da Vinci Tiny | Mali-G76 MP16 |
Kirin 990 | 1x Da Vinci Lite + 1x Da Vinci Tiny | Mali-G76 MP16 |
Kirin 980 | 2x Cambricon 1H | Mali-G76 MP10 |
Kirin 970 | 1x Cambricon 1A | Mali-G72 MP12 |
Kirin 810 | 1x Da Vinci Tiny | Mali-G52 MP6 |
Kirin 710 | Mali-G51 MP4 |
华为的 SDK 是 HiAI DDK:官网
Qualcomm DSP
外称 Hexagon,型号是 QDSP6
Version | Date | |
---|---|---|
QDSP6 V1 | 2006 | |
QDSP6 V2 | 2007 | |
QDSP6 V3 | 2009 | |
QDSP6 V4 | 2010-2011 | |
QDSP6 V5 | 2013 | Snapdragon 800 |
QDSP6 V6 68X | 2016-2019 |
比较新的 SoC 都包含 Hexagon 600 系列的 DSP了
Snapdragon | CPU | GPU | DSP |
---|---|---|---|
800 | Krait 400 | Adreno 330 | Hexagon QDSP6 V5 |
801/805/808/810 | Hexagon 500 | ||
820/821 | Hexagon 600 | ||
835 | Kryo 280 (A73) | Adreno 540 | Hexagon 682 |
845 | Kryo 385 (A55+A75) | Adreno 630 | Hexagon 685 |
855 | Kryo 485 (A55+A76) | Adreno 640 | Hexagon 690 |
855+ | Kryo 485 (A55+A76) | Adreno 640 | Hexagon 690 |
高通的 SDK 是 SNPE(现在改名了):官网
MediaTek APU
MTK 的加速器叫做 APU,资料较少没有找到 SDK 信息,目前已经做到 3.0 版本了
GPU | APU | |
---|---|---|
Helio P1X/P2X | Mali-T8X0 / PowerVR GE8320 | 无 |
Helio X1X/X2X | Mali-T880 / PowerVR G6200 | 无 |
Helio P30/P35 | Mali-G71 | 1x P5 DSP |
Helio X30 | PowerVR 7XTP | 1x P5 DSP |
Helio P60/P65/P70 | Mali-G52 / Mali-G72 | 2x P6 DSP |
Helio M70 | Mali-G77 | 未知 (3.0) |
Helio P90 | PowerVR GM9446 | 2x R6 DSP (2.0) |
Helio G90 | Mali-G76 | 2x R6 DSP (2.0) |