Un enfoque práctico para armar la optimización de neón

Arm Neon se introdujo para mejorar la codificación / decodificación multimedia, la interfaz de usuario, los gráficos y las funciones relacionadas con los juegos que se ejecutan en dispositivos móviles. A lo largo de los años, se ha utilizado para acelerar los algoritmos y las capacidades de procesamiento de señales, así como para acelerar los avances en el aprendizaje profundo y las aplicaciones relacionadas con la inteligencia artificial, como el reconocimiento de voz, el reconocimiento facial y la visión por computadora, así como las aplicaciones multimedia de audio y video. .Campo de arroz. Neon Architecture es de código abierto, lo que brinda flexibilidad y soporte para diferentes tipos de datos. Esta es una de las principales razones por las que las aplicaciones pueden aprovechar estas funciones.

Este blog se proporciona como una guía con puntos prácticos sobre la arquitectura ArmNeon que pueden utilizar todos los desarrolladores de software.

Armar arquitectura Neon entre versiones

El concepto de neón es el mismo en múltiples versiones, pero existen diferencias en cómo se implementa. Las principales diferencias entre las arquitecturas de neón Armv7 (32 bits) y Armv8 (64 bits) son:

  • Armv7 tiene Q0 a Q15, registros de 128 bits, a los que también se puede acceder como registros D0 a D31 de 64 bits o registros S0 a S31 de 32 bits. Al igual que Armv8, tiene registros de 32X128 bits, denominados V0 a V31. Pero D0-D63 ya no está allí. Solo hay D0 a D31 y S0 a S31. Esto se debe a que el registro de palabra doble o VFP se encuentra en la parte inferior de los registros de palabra cuádruple V0 a V31. Por lo tanto, la instrucción funciona en la mitad inferior, pero automáticamente pone a cero la parte superior del registro V.
  • Dado que la disposición del registro se ha cambiado en Armv8, la instrucción de reemplazo (clasificación de datos como zip, unzip, transpose) se ha cambiado y la operación es diferente. Las instrucciones de permutación funcionan en el lugar en Armv7, pero Armv8 requiere un registro de destino.
  • Debido a los cambios arquitectónicos anteriores, el código de ensamblaje de ArmV8 no es compatible con versiones anteriores. Los desarrolladores necesitan volver a analizar y, a menudo, tienen una solución completamente diferente.
Soporte de desarrollo de arm neon

Arm y su comunidad ofrecen una variedad de posibilidades para que los desarrolladores aprovechen la tecnología Neon.

  • Función de vectorización automática por compilador: El compilador Arm tiene la capacidad de generar código SIMD optimizado para la utilización de neón. En términos de tiempo y costo de diseño, esta característica es una ventaja para los desarrolladores. Sin embargo, para algoritmos complejos, el código generado por el compilador no está completamente optimizado. En tales casos, el desarrollador debe investigar la esencia del ensamblaje ajustado manualmente. La vectorización automática incluye:

Vectorización de bucle: Expanda el ciclo para reducir el número de iteraciones y realizar más operaciones en cada iteración.

Vectorización de procesamiento paralelo a nivel de superpalabra (SLP): Agrupa operaciones escalares para aprovechar las instrucciones SIMD avanzadas de ancho completo. Neon está habilitado de forma predeterminada en la arquitectura Armv8. Los desarrolladores pueden apuntar a Armv8 AArch64 con un objetivo habilitado para neón, o especificar la CPU en el estado AArch32 de Cortex-A53.

Neon es opcional en la arquitectura Armv7. Los desarrolladores pueden habilitar el módulo Neon con opciones de compilador como: -mcpu, -marcha Cuando -mfpu .. Además, la vectorización automática está habilitada de forma predeterminada en niveles de optimización más altos (-O2 y superiores).Cuando -fno-vectorizar La configuración ayuda a deshabilitar la vectorización automática. Nivel de optimización: en O1, la vectorización automática está desactivada de forma predeterminada. -fvectorizar Puede utilizar la opción para habilitar la vectorización automática. En el nivel de optimización -O0, la vectorización automática siempre está deshabilitada.Si se especifica -fvectorizar Si es opcional, el compilador lo ignora.

  • Funciones integradas de neón: Las funciones integradas de Neon permiten a los desarrolladores implementar código que está más optimizado para la arquitectura Neon que el código generado por el compilador. Esta es una ventaja para los desarrolladores porque conocen la aplicación mejor que el compilador. Puede consultar el archivo de encabezado arm_neon.h para las funciones integradas de Neon, que son un conjunto de funciones C y C ++ compatibles con el compilador Arm y GCC. Estas funciones integradas aceleran el desarrollo al proporcionar los mismos grados de libertad que las instrucciones de ensamblaje de neón, con el compilador manejando la asignación de registros. En la etapa de compilación, las funciones integradas de neón se reemplazan con las instrucciones de neón apropiadas o un conjunto de instrucciones de neón. Aquí hay un ejemplo de una función incorporada de neón:
  • Ensamblador de neón codificado a mano: Los desarrolladores de programas experimentados pueden aprovechar las instrucciones de ensamblaje para generar código optimizado cuando el rendimiento es importante. En algunas áreas del algoritmo, las instrucciones Arm y Neon se pueden usar en paralelo para operaciones independientes. El programa de montaje de neón se ve así:
  • Biblioteca habilitada para neón: Arm y su comunidad ya ofrecen bibliotecas de código abierto que utilizan Neon, y los desarrolladores pueden conectar estas bibliotecas directamente en su entorno de desarrollo. Hay pocas bibliotecas de este tipo.
    • Arm Compute Library: esta biblioteca es una colección de funciones de bajo nivel optimizadas para la arquitectura Arm CPU y GPU para procesamiento de imágenes, visión por computadora y aprendizaje automático.
    • Ne10: La biblioteca C de código abierto alojada por Arm en GitHub es una función general de procesamiento intensivo que está muy optimizada para Arm. Ne10 es una estructura modular que consta de varias bibliotecas pequeñas.
    • Libyuv: un proyecto de código abierto que incluye capacidades de conversión y escalado YUV.
    • Skia: una biblioteca de gráficos 2D de código abierto que se utiliza como motor gráfico para navegadores web y sistemas operativos.
Rendimiento mejorado del brazo y el neón

A partir de varios ejemplos, los desarrolladores han demostrado que el conjunto de instrucciones Neon SIMD se puede utilizar para lograr muy buenas optimizaciones de rendimiento. Sin embargo, el nivel de optimización alcanzado depende del código, es decir, cuánta vectorización es posible. por ejemplo. Para los filtros IIR, existen dependencias de muestra de salida calculadas previamente. En tales casos, el neón no puede proporcionar la cantidad de mejora en comparación con filtros como FIR a los que se puede aplicar la vectorización pura.

El siguiente ejemplo detalla el nivel de optimización que se puede lograr con la instrucción Neon en comparación con la instrucción Arm.

  • Para el procesamiento de códec de video complejo (mpeg4), Neon ofrece una mejora de rendimiento de 1.6-2.5x sobre Arm11.
  • En el procesamiento de audio (AAC, algoritmo de reconocimiento de voz) FFT, el neón proporciona cuatro veces la mejora del rendimiento (3,8 us) que Arm11 (15,2 us).
  • En ffmpeg FFT, el neón proporciona 12 veces el aumento de rendimiento de Arm11

El siguiente ejemplo muestra las mejoras de rendimiento que puede lograr Neon. Programa de operación de suma de producto de matriz simple (c[i] = c[i]+ un[i] * NS[i] ).

El código de ensamblaje Arm11 escrito a mano para el programa C anterior se muestra a continuación.

El código de ensamblaje de Neon escrito a mano para el programa C anterior se muestra a continuación.

Ahora calculemos los ciclos tanto para el código Arm como para el código Neon, dado que el número de bucles es 256.

Cirugía Montaje del brazo (ciclo) Neon SIMD (ciclo)
Cargar y guardar (256/2) * (2 + 2 + 2 + 2) = 1024 (256/8) * (2 + 2 + 2 + 2) = 256
Sekiwa (256/2) * (3 + 1 + 1 + 1 + 3 + 1 + 1 + 1) = 1536 (256/8) * (2 + 1 + 1 + 2 + 1 + 1) = 256
Gestión de sucursales (255/2) * 1 + 4 = 131 (255/8) * 1 + 4 = 35
total 2691 547

Este ejemplo muestra que el neón puede mejorar aún más el rendimiento del programa. 70% Compare con el código de montaje del brazo.