免责声明:这篇文章的所有内容都是我个人调研得到的,来源是网络的各种资料。难免出现各种错误,有的内容可能吸收了别人的工作成果。如果遇到问题,欢迎提出,我会及时更改。
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 | -march=armv7-a -mthumb (-mfloat-abi=softfp) |
|
https://android.googlesource.com/platform/ndk/+/ndk-r12-release/docs/HardFloatAbi.md | removed in NDK r12 | |
arm64-v8a | (-march=aarch64 -mfloat-abi=hard) |
|
x86 | (-march=i686 -mtune=intel -mssse3 -mfpmatch=sse -m32) |
|
x86_64 | (-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel) |
Android 已经放弃支持 armeabi
,mips
,mips64
了,简单来说,现在 Android 仅支持 ARM 和 x86 了,而 ARM 也仅仅支持 v7a
和 v8a
。
ARM CPU
我们常说的高通 855,麒麟980 不是 CPU 是 SoC(System On Chip),SoC 除了 CPU 外,还有 GPU,还有可选的浮点数加速器,专用于深度模型的加速器,这些都和本文相关。除此以外,SoC 还包括运存,基带芯片等等,这些和深度学习关系不大。
ARM 和各个 SoC 的关系:所有 ARM 的 CPU 都是 ARM 公司授权的,授权的形式是 IP 核,各个商场得到授权,生产自己的 SoC。
ARM 架构
历史上,ARM 定义了 ARMv1
,ARMv2
,ARMv3
,ARMv4
,ARMv4T
,ARMv5TE
,ARMv6
这些架构,都已经过时了。
在定义 ARMv7
时,引入了 profile 的概念,将 ARMv7
划分为:
-
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 中设置 build.gradle
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 中设置
cmake -DANDROID_ABI=arm64-v8a ...
cmake -DANDROID_ABI=x86_64 ...
在 ndk-build 中设置 Application.mk
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
ARM 的浮点数运算
首先介绍 VFP(Vector Floating Point)和 Neon:两者都是浮点数运算器,在 ARMv7-A
中浮点数运算器是可选的(即有的手机没有)。VFP 和 Neon 实现细节不同, Neon 支持并行运算,速度更快。在 ARMv8-A
中,Neon 是必备的。此外,Android ABI 约束 armeabi-v7a
必须支持 vfp,Android 6 约束 ARMv-A
必须包含 Neon。所以 ARMv8-A
可以直接使用 Neon,ARMv7-A
需要判断使用
对于 ARMv7-A
来说,另一个需要关注的问题是浮点数运算的函数调用方式,google 在 NDKr12 中移除了 hard-float 的支持,浮点数会先转换为整数,放在整数寄存器里,然后计算,这里会损失性能。曾经有 armeabi-v7a-hard
这一 ABI 可以解决此问题,不过已经废弃了。
FPU | Function call | |
---|---|---|
yes | hard-float | |
armeabi-v7a | optional | soft-float |
arm64-v8a | yes | hard-float |
浮点数调用以及浮点数运算器总结如下,表格从上到下, 运算速度递增。
Type | Flag | Register, FPU | Android | 备注 |
---|---|---|---|---|
soft | -mfloat-abi=soft |
Integer | 不使用 FPU | |
softfp | -mfloat-abi=softfp |
Integer + VFP | 默认使用 vfpv3-d16 | |
softfp-vfp | -mfloat-abi=softfp -mfpu=vfpv3-d16 |
Integer + VFP | armv7a 默认 | 同 softfp |
softfp-neon | -mfloat-abi=softfp -mfpu=neon |
Integer + Neon | armv7a 有 neon 时 | |
hard | -mfloat-abi=hard |
Float + VFP | armv7-a 无法用 | 默认使用 vfpv3-d16 |
hard-neon | -mfloat-abi=hard -mfpu=neon |
Float + Neon | armv8-a 默认 |
最简单的加速方法就是开启 Neon。
在 gradle 中开启,设置 build.gradle
android {
defautConfig {
externalNativeBuild {
cmake {
arguments "-DANDROID_ARM_NEON=ON"
}
}
}
}
在 cmake 中开启
cmake -DANDROID_ARM_NEON=ON ...
或者编译 CMakeLists.txt
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 中开启
LOCAL_ARM_NEON := true
# or add .neon
LOCLA_SRC_FILES := foo.c.neon bar.c
因为 ARMv7-A
的兼容性问题,不是所有的手机都支持 Neon,需要在运行时进行判断
java 版本的检测示例:
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 代码的检测示例
#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
}
最后需要说明的是,ARMv8-A
的 Neon 兼容 ARMv7-A
的(写一套代码是可行的),而且性能更好,功能更强大:参考 ARM提供的资料

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) |
网友评论