CPU 特征应对

ABI:使用预处理器预定义的宏

在构建阶段使用 #ifdef 与以下各项来确定 ABI 最为方便:

  • __arm__ 用于 32 位 ARM
  • __aarch64__ 用于 64 位 ARM
  • __i386__ 用于 32 位 X86
  • __x86_64__ 用于 64 位 X86

请注意:32 位 X86 被称为 __i386__,而不是 __x86__,这可能与您预想的有所不同!

CPU 核心计数:使用 libc 的 sysconf(3)

sysconf(3) 即可以查询 _SC_NPROCESSORS_CONF(系统中的 CPU 核心数),也可以查询 _SC_NPROCESSORS_ONLN(当前在线的 CPU 核心数)。

特征:使用 libc 的 getauxval(3)

自 API 级别 18 开始,Android 的 C 库开始支持 getauxval(3)AT_HWCAPAT_HWCAP2 参数能够返回列出特定 CPU 特征的掩码。 请参阅 NDK 中的各种 hwcap.h 标头以获取常量进行比较,如用于 arm64 SHA512 指令的HWCAP_SHA512,或用于 arm Thumb 整数除法指令的 HWCAP_IDIVT

Google cpu_features 库

AT_HWCAP 的一个问题是有时设备会出错。 例如,一些老旧设备宣称拥有整数除法指令,但实际上并没有。

Google 的 cpu_features 库凭借其对特定 SoC 的了解(通过解析 /proc/cpuinfo,掌握特定的 SoC),解决了此类问题。

另一个解决方法是为 SIGILL 安装信号处理程序,然后直接执行相关指令。 例如,BoringSSL/OpenSSL 使用的就是这种方法。

NDK cpufeatures 库

NDK 提供名为 cpufeatures 的小型库,其功能类似于 getauxval(3),但它也可用于 API 级别 18 之前的版本。 与其他 cpu_features 库不同,它对特定 SoC 并无额外的了解。

NDK cpufeatures API

uint64_t android_getCpuFeatures();

返回一组位标记,每个标记代表一个 CPU 系列特定的特征。 本部分其余内容介绍各个系列的特征。

32 位 ARM CPU 系列

以下标记适用于 32 位 ARM CPU 系列:

ANDROID_CPU_ARM_FEATURE_VFPv2
表示设备的 CPU 支持 VFPv2 指令集。 大多数 ARMv6 CPU 都支持此指令集。
ANDROID_CPU_ARM_FEATURE_ARMv7
表示设备的 CPU 支持 armeabi-v7a ABI 所支持的 ARMv7-A 指令集。 此指令集同时支持 Thumb-2 和 VFPv3-D16 指令。 此返回值还表示支持 VFPv3 硬件 FPU 指令集扩展。
ANDROID_CPU_ARM_FEATURE_VFPv3
表示设备的 CPU 支持 VFPv3 硬件 FPU 指令集扩展。

此值等同于 VFPv3-D16 指令集,只提供 16 个硬件双精度 FP 寄存器。

ANDROID_CPU_ARM_FEATURE_VFP_D32
表示设备的 CPU 支持 32 个(而不是 16 个)硬件双精度 FP 寄存器。即使有 32 个硬件双精度 FP 寄存器,也只有 32 个单精度寄存器映射至同一寄存器库。
ANDROID_CPU_ARM_FEATURE_NEON
表示设备的 CPU 支持 ARM Advanced SIMD (NEON) 矢量指令集扩展。 请注意,ARM 要求这些 CPU 也要实现 VFPv3-D32,以提供 32 个硬件 FP 寄存器(与 NEON 单元共享)。
ANDROID_CPU_ARM_FEATURE_VFP_FP16
表示设备的 CPU 支持用于在 16 位寄存器上执行浮点运算的指令。 该功能是 VFPv4 规范的一部分。
ANDROID_CPU_ARM_FEATURE_VFP_FMA
表示设备的 CPU 支持 VFP 指令集融合的乘积累加扩展。 它也是 VFPv4 规范的一部分。
ANDROID_CPU_ARM_FEATURE_NEON_FMA
表示设备的 CPU 支持 NEON 指令集融合的乘积累加扩展。 它也是 VFPv4 规范的一部分。
ANDROID_CPU_ARM_FEATURE_IDIV_ARM
表示设备的 CPU 支持 ARM 模式下的整数除法。 仅适用于更高型号的 CPU,例如 Cortex-A15。
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2
表示设备的 CPU 支持 Thumb-2 模式下的整数除法。 仅适用于更高型号的 CPU,例如 Cortex-A15。
ANDROID_CPU_ARM_FEATURE_iWMMXt
表示设备的 CPU 支持可添加 MMX 寄存器和指令的指令集扩展。 该功能仅适用于少数几个基于 XScale 的 CPU。
ANDROID_CPU_ARM_FEATURE_LDREX_STREX
表示设备的 CPU 支持自 ARMv6 后可用的 LDREX 和 STREX 指令。借助专用监视器,这些指令结合起来可在内存中提供原子更新。

64 位 ARM CPU 系列

以下标记适用于 64 位 ARM CPU 系列:

ANDROID_CPU_ARM64_FEATURE_FP
表示设备的 CPU 具有浮点单元 (FPU)。 所有 Android ARM64 设备都必须支持此功能。
ANDROID_CPU_ARM64_FEATURE_ASIMD
表示设备的 CPU 具有高级 SIMD (ASIMD) 单元。 所有 Android ARM64 设备都必须支持此功能。
ANDROID_CPU_ARM64_FEATURE_AES
表示设备的 CPU 支持 AES 指令。
ANDROID_CPU_ARM64_FEATURE_CRC32
表示设备的 CPU 支持 CRC32 指令。
ANDROID_CPU_ARM64_FEATURE_SHA1
表示设备的 CPU 支持 SHA1 指令。
ANDROID_CPU_ARM64_FEATURE_SHA2
表示设备的 CPU 支持 SHA2 指令。
ANDROID_CPU_ARM64_FEATURE_PMULL
表示设备的 CPU 支持 64 位 PMULLPMULL2 指令。

32 位 x86 CPU 系列

以下标记适用于 32 位 x86 CPU 系列。

ANDROID_CPU_X86_FEATURE_SSSE3
表示设备的 CPU 支持 SSSE3 指令扩展集。

ANDROID_CPU_X86_FEATURE_POPCNT
表示设备的 CPU 支持 POPCNT 指令。

ANDROID_CPU_X86_FEATURE_MOVBE
表示设备的 CPU 支持 MOVBE 指令。 此指令特定于某些 Intel IA-32 CPU,例如 Atom。

对于没有所列出扩展的 CPU 系列,android_getCpuFeatures() 返回 0

int android_getCpuCount(void);

返回系统中的 CPU 核心数,可能高于实际在线的核心数。

AndroidCpuFamily android_getCpuFamily();

返回以下其中一个代表设备所支持 CPU 系列/架构的常量:

  • ANDROID_CPU_FAMILY_ARM
  • ANDROID_CPU_FAMILY_X86
  • ANDROID_CPU_FAMILY_ARM64
  • ANDROID_CPU_FAMILY_X86_64

对于 64 位系统上的 32 位可执行文件,此函数返回 32 位架构。

将 NDK cpufeatures 与 ndk-build 搭配使用

cpufeatures 库可用作导入模块。 要使用此库,请执行以下操作:

  • 添加 cpufeaturesLOCAL_STATIC_LIBRARIES

  • #include <cpu-features.h> 加入您的源代码。

  • Android.mk 的最后导入 android/cpufeatures

以下为 Android.mk 导入 cpufeatures 的示例:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := your-module-name
LOCAL_SRC_FILES := ...
LOCAL_STATIC_LIBRARIES := cpufeatures
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/cpufeatures)