CPU 機能を使用する

ABI: プリプロセッサの定義済みマクロを使用する

通常は、#ifdef と以下を併用して、ビルド時に ABI を指定しておくことをおすすめします。

  • __arm__(32 ビット ARM の場合)
  • __aarch64__(64 ビット ARM の場合)
  • __i386__(32 ビット x86 の場合)
  • __x86_64__(64 ビット x86 の場合)

32 ビット x86 は、想定される「__x86__」ではなく「__i386__」になる点に注意してください。

CPU コア数: libc の sysconf(3) を使用する

sysconf(3) は、_SC_NPROCESSORS_CONF(システム内の CPU コアの数)と _SC_NPROCESSORS_ONLN(現在オンラインになっている CPU コアの数)の両方をクエリできます。

機能: libc の getauxval(3) を使用する

API レベル 18 以降の場合、Android の C ライブラリで getauxval(3) を使用することができます。AT_HWCAP 引数と AT_HWCAP2 引数は、CPU 固有の機能のリストを示すビットマスクを返します。NDK 内の各種 hwcap.h ヘッダーで、各機能を示す定数を確認できます(arm64 の SHA512 命令の場合は HWCAP_SHA512、arm の Thumb 整数除算命令の場合は HWCAP_IDIVT)。

Google cpu_features ライブラリ

AT_HWCAP の問題の 1 つとして、デバイスが誤って解釈されることがあります。たとえば、古いデバイスの場合に「整数除算命令をサポートしている」と示されても、実際にはサポートしていないことがあります。

Google の cpu_features ライブラリは、各 SoC に関する独自のナレッジを適用することで(/proc/cpuinfo を解析して、対象の SoC を判別することで)、このような問題を回避します。

別の対応策としては、SIGILL のシグナル ハンドラをインストールして、対象の命令を試行するというものもあります。たとえば、BoringSSL / OpenSSL はこの方法を使用しています。

NDK cpufeatures ライブラリ

NDK には、getauxval(3) と同様の機能を実現する「cpufeatures」という小さなライブラリが用意されています。ただし、このライブラリは、API レベル 18 より前のプラットフォーム バージョンでも動作します。他の cpu_features ライブラリとは異なり、このライブラリには、各 SoC に関する追加のナレッジは含まれていません。

NDK cpufeatures API

uint64_t android_getCpuFeatures();
    

ビットフラグのセットを返します。フラグごとに、CPU ファミリー固有の機能を 1 つ示します。このセクションの残りの部分では、各ファミリーの機能について説明します。

32 ビット ARM CPU ファミリー

32 ビット ARM CPU ファミリーでは、次のフラグを使用できます。

ANDROID_CPU_ARM_FEATURE_VFPv2
デバイスの CPU が VFPv2 命令セットをサポートしていることを示します。ほとんどの ARMv6 CPU はこの命令セットをサポートしています。
ANDROID_CPU_ARM_FEATURE_ARMv7
armeabi-v7a ABI の場合と同様に、デバイスの CPU が ARMv7-A 命令セットをサポートしていることを示します。この命令セットは、Thumb-2 命令と VFPv3-D16 命令の両方をサポートします。この戻り値は、VFPv3 ハードウェア FPU 命令セット拡張機能をサポートしていることも示します。
ANDROID_CPU_ARM_FEATURE_VFPv3
デバイスの CPU が VFPv3 ハードウェア FPU 命令セット拡張機能をサポートしていることを示します。

この値は VFPv3-D16 命令セットと同値であり、サポートされるハードウェア倍精度 FP レジスタは 16 個だけになります。

ANDROID_CPU_ARM_FEATURE_VFP_D32
デバイスの CPU が、16 個ではなく 32 個のハードウェア倍精度 FP レジスタをサポートしていることを示します。32 個のハードウェア倍精度 FP レジスタがある場合でも、32 個の単精度レジスタだけが同じレジスタバンクにマッピングされます。
ANDROID_CPU_ARM_FEATURE_NEON
デバイスの CPU が ARM Advanced SIMD(NEON)ベクター命令セット拡張機能をサポートしていることを示します。このような CPU の場合、ARM は、VFPv3-D32 も実装することを必須としています。VFPv3-D32 は、32 個のハードウェア FP レジスタをサポートします(NEON ユニットと共有されます)。
ANDROID_CPU_ARM_FEATURE_VFP_FP16
デバイス CPU が、16 ビットレジスタ上で浮動小数点演算を実行する命令をサポートしていることを示します。この機能は VFPv4 仕様の一部です。
ANDROID_CPU_ARM_FEATURE_VFP_FMA
デバイスの CPU が VFP 命令セットの FMAC(Fused Multiply-ACcumulate)拡張機能をサポートしていることを示します。この機能は VFPv4 仕様の一部です。
ANDROID_CPU_ARM_FEATURE_NEON_FMA
デバイスの CPU が NEON 命令セットの FMAC(Fused Multiply-ACcumulate)拡張機能をサポートしていることを示します。この機能は VFPv4 仕様の一部です。
ANDROID_CPU_ARM_FEATURE_IDIV_ARM
デバイスの CPU が ARM モードの整数除算をサポートしていることを示します。使用できるのは、Cortex-A15 など、新しい CPU に限られます。
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2
デバイスの CPU が Thumb-2 モードの整数除算をサポートしていることを示します。使用できるのは、Cortex-A15 など、新しい CPU に限られます。
ANDROID_CPU_ARM_FEATURE_iWMMXt
デバイスの CPU が、MMX レジスタおよび MMX 命令を追加する命令セット拡張機能をサポートしていることを示します。この機能を使用できるのは、ごく一部の XScale ベース CPU に限られます。
ANDROID_CPU_ARM_FEATURE_LDREX_STREX
デバイスの CPU が、ARMv6 以降で使用できる LDREX 命令および STREX 命令をサポートしていることを示します。この 2 つの命令を組み合わせることで、排他的モニターに基づくメモリのアトミック アップデートがサポートされます。

64 ビット ARM CPU ファミリー

64 ビット ARM CPU ファミリーでは、次のフラグを使用できます。

ANDROID_CPU_ARM64_FEATURE_FP
デバイスの CPU が浮動小数点演算ユニット(FPU)を持っていることを示します。Android ARM64 デバイスは、必ずこの機能をサポートする必要があります。
ANDROID_CPU_ARM64_FEATURE_ASIMD
デバイスの CPU が Advanced 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 ビットの PMULL 命令と PMULL2 命令をサポートしていることを示します。

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 命令をサポートしていることを示します。この命令は、Atom など、一部の Intel IA-32 CPU に固有のものです。

拡張機能のリストがない 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-build で NDK cpufeatures を使用する

cpufeatures ライブラリはインポート モジュールとして使用できます。使用手順は次のとおりです。

  • cpufeaturesLOCAL_STATIC_LIBRARIES に追加します。

  • ソースコードに #include <cpu-features.h> を追加します。

  • Android.mk の末尾に android/cpufeatures をインポートします。

cpufeatures をインポートする Android.mk の例を以下に示します。

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)