Cómo tratar con funciones de CPU

ABI: Cómo usar las macros predefinidas del procesador

Por lo general, lo más conveniente es determinar la ABI en el momento de la compilación mediante #ifdef junto con:

  • __arm__ para ARM de 32 bits
  • __aarch64__ para ARM de 64 bit
  • __i386__ para x86 de 32 bits
  • __x86_64__ para x86 de 64 bits

Ten en cuenta que el x86 de 32 bits se denomina __i386__, no __x86__ como podrías esperar.

Recuentos de núcleos de CPU: Uso de sysconf(3) de libc

sysconf(3) te permite consultar tanto _SC_NPROCESSORS_CONF (la cantidad de núcleos de CPU en el sistema) como _SC_NPROCESSORS_ONLN (la cantidad de núcleos de CPU que se encuentran en línea actualmente.

Funciones: Uso de getauxval(3) de libc

A partir del nivel de API 18, getauxval(3) está disponible en la biblioteca C de Android. Los argumentos AT_HWCAP y AT_HWCAP2 devuelven máscaras de bits en las que se enumeran las funciones específicas de la CPU. Consulta los distintos encabezados hwcap.h en el NDK para conocer las constantes que usarás para comparar, como HWCAP_SHA512 para instrucciones SHA512 de arm64 o HWCAP_IDIVT para las instrucciones de división de enteros de Thumb de ARM.

La biblioteca cpu_features de Google

Un problema que presenta AT_HWCAP es que, a veces, los dispositivos brindan información errónea. Por ejemplo, algunos dispositivos antiguos declaran que tienen instrucciones de división de enteros, cuando no es así.

La biblioteca cpu_features de Google soluciona esos problemas al aplicar su propio conocimiento sobre SoC específicos (para ello, analiza /proc/cpuinfo y resuelve el SoC específico en cuestión).

Una solución alternativa es instalar un controlador de señales para SIGILL y simplemente intentar ejecutar la instrucción en cuestión. Por ejemplo, BoringSSL/OpenSSL usa esta técnica.

La biblioteca cpufeatures del NDK

El NDK proporciona una biblioteca pequeña llamada cpufeatures que ofrece una funcionalidad similar a getauxval(3), pero que también funciona en niveles de API anteriores al 18. A diferencia de la otra biblioteca cpu_features, esta no tiene conocimiento adicional sobre los SoC.

API cpufeatures del NDK

uint64_t android_getCpuFeatures();
    

Devuelve un conjunto de indicadores de bits, y cada uno representa una función específica de la familia de CPU. En el resto de la sección, se proporciona información sobre las funciones para las respectivas familias.

Familia de CPU ARM de 32 bits

Los siguientes indicadores están disponibles para la familia de CPU ARM de 32 bits:

ANDROID_CPU_ARM_FEATURE_VFPv2
Indica que la CPU del dispositivo admite el conjunto de instrucciones VFPv2. La mayoría de las CPU ARMv6 admiten este conjunto de instrucciones.
ANDROID_CPU_ARM_FEATURE_ARMv7
Indica que la CPU del dispositivo admite el conjunto de instrucciones ARMv7-A, al igual que la ABI armeabi-v7a. Este conjunto de instrucciones admite las instrucciones Thumb-2 y VFPv3-D16. Este valor de retorno también indica compatibilidad con la extensión del conjunto de instrucciones de FPU asistidas por hardware VFPv3.
ANDROID_CPU_ARM_FEATURE_VFPv3
Indica que la CPU del dispositivo admite la extensión del conjunto de instrucciones de FPU asistidas por hardware VFPv3.

Este valor es equivalente al conjunto de instrucciones VFPv3-D16, que solo proporciona 16 registros de punto flotante de doble precisión asistidos por hardware.

ANDROID_CPU_ARM_FEATURE_VFP_D32
Indica que la CPU del dispositivo admite 32 registros de punto flotante de doble precisión asistidos por hardware, en lugar de 16. Incluso cuando haya 32 registros de punto flotante de doble precisión asistidos por hardware, solo habrá 32 registros de precisión sencilla asignados a los mismos bancos de registros.
ANDROID_CPU_ARM_FEATURE_NEON
Indica que la CPU del dispositivo admite la extensión del conjunto de instrucciones vectoriales SIMD avanzado de ARM (NEON). Ten en cuenta que ARM exige que estas CPU también implementen VFPv3-D32, que proporciona 32 registros de punto flotante asistidos por hardware (compartidos con la unidad NEON).
ANDROID_CPU_ARM_FEATURE_VFP_FP16
Indica que la CPU del dispositivo admite instrucciones para realizar operaciones de punto flotante en registros de 16 bits. Esta función forma parte de la especificación de VFPv4.
ANDROID_CPU_ARM_FEATURE_VFP_FMA
Indica que la CPU del dispositivo admite la extensión fusionada multiply-accumulate para el conjunto de instrucciones de VFP. También forma parte de la especificación de VFPv4.
ANDROID_CPU_ARM_FEATURE_NEON_FMA
Indica que la CPU del dispositivo admite la extensión fusionada multiply-accumulate para el conjunto de instrucciones de NEON. También forma parte de la especificación de VFPv4.
ANDROID_CPU_ARM_FEATURE_IDIV_ARM
Indica que la CPU del dispositivo admite la división de enteros en modo ARM. Solo está disponible en las CPU más modernas, como Cortex-A15.
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2
Indica que la CPU del dispositivo admite la división de enteros en modo Thumb-2. Solo disponible en las CPU más modernas, como Cortex-A15.
ANDROID_CPU_ARM_FEATURE_iWMMXt
Indica que la CPU del dispositivo admite una extensión de conjunto de instrucciones que agrega instrucciones y registros MMX. Esta función solo está disponible en algunas CPU basadas en XScale.
ANDROID_CPU_ARM_FEATURE_LDREX_STREX
Indica que la CPU del dispositivo admite instrucciones LDREX y STREX disponibles desde ARMv6. En conjunto, estas instrucciones proporcionan actualizaciones atómicas en la memoria con la ayuda de un monitor exclusivo.

Familia de CPU ARM de 64 bits

Los siguientes indicadores están disponibles para la familia de CPU ARM de 64 bits:

ANDROID_CPU_ARM64_FEATURE_FP
Indica que la CPU del dispositivo tiene una unidad de punto flotante (FPU). Todos los dispositivos ARM64 de Android deben admitir esta función.
ANDROID_CPU_ARM64_FEATURE_ASIMD
Indica que la CPU del dispositivo tiene una unidad SIMD avanzado (ASIMD). Todos los dispositivos ARM64 de Android deben admitir esta función.
ANDROID_CPU_ARM64_FEATURE_AES
Indica que la CPU del dispositivo admite instrucciones AES.
ANDROID_CPU_ARM64_FEATURE_CRC32
Indica que la CPU del dispositivo admite instrucciones CRC32.
ANDROID_CPU_ARM64_FEATURE_SHA1
Indica que la CPU del dispositivo admite instrucciones SHA1.
ANDROID_CPU_ARM64_FEATURE_SHA2
Indica que la CPU del dispositivo admite instrucciones SHA2.
ANDROID_CPU_ARM64_FEATURE_PMULL
Indica que la CPU del dispositivo admite instrucciones PMULL y PMULL2 de 64 bits.

Familia de CPU x86 de 32 bits

Los siguientes indicadores están disponibles para la familia de CPU x86 de 32 bits:

ANDROID_CPU_X86_FEATURE_SSSE3
Indica que la CPU del dispositivo admite la extensión del conjunto de instrucciones SSSE3.
ANDROID_CPU_X86_FEATURE_POPCNT
Indica que la CPU del dispositivo admite la instrucción POPCNT.
ANDROID_CPU_X86_FEATURE_MOVBE
Indica que la CPU del dispositivo admite la instrucción MOVBE. Esta instrucción es específica de algunas CPU Intel IA-32, como Atom.

android_getCpuFeatures() devuelve 0 para familias de CPU para las cuales no se enumeran extensiones.

int android_getCpuCount(void);
    

Devuelve la cantidad de núcleos de CPU en el sistema, que puede ser mayor que la cantidad real que aparece en línea.

AndroidCpuFamily android_getCpuFamily();
    

Devuelve una de las siguientes constantes que representan la familia y la arquitectura de la CPU que admite el dispositivo:

  • ANDROID_CPU_FAMILY_ARM
  • ANDROID_CPU_FAMILY_X86
  • ANDROID_CPU_FAMILY_ARM64
  • ANDROID_CPU_FAMILY_X86_64

Para un ejecutable de 32 bits en un sistema de 64 bits, esta función solo devuelve la arquitectura de 32 bits.

Cómo usar cpufeatures del NDK con ndk-build

La biblioteca cpufeatures está disponible como un módulo de importación. Para usarla:

  • Agrega cpufeatures a LOCAL_STATIC_LIBRARIES.

  • #include <cpu-features.h> en tu código de fuente.

  • Importa android/cpufeatures al final de tu archivo Android.mk.

A continuación, se muestra un ejemplo de archivo Android.mk que importa 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)