c++ c floating-point arm neon

c++ - Cortex A9 NEON vs VFP uso confusión



floating-point arm (3)

... las publicaciones en el foro y en el blog y todos parecen estar de acuerdo en que usar NEON es mejor que usar VFP o al menos mezclar NEON (por ejemplo, usar los instrumentos para implementar algunos algos en SIMD) y VFP no es una buena idea

No estoy seguro de que esto sea correcto. De acuerdo con ARM en Introducción al artículo de desarrollo de NEON | Registros de NEON :

El banco de registros NEON consta de 32 registros de 64 bits. Si se implementan Advanced SIMD y VFPv3, comparten este banco de registros. En este caso, VFPv3 se implementa en la forma VFPv3-D32 que admite 32 registros de punto flotante de doble precisión. Esta integración simplifica la implementación del soporte de cambio de contexto, porque las mismas rutinas que guardan y restauran el contexto VFP también guardan y restauran el contexto NEON.

La unidad NEON puede ver el mismo banco de registros que:

  • Dieciséis registros quadword de 128 bits, Q0-Q15
  • treinta y dos registros de doble palabra de 64 bits, D0-D31.

Los registros NEON D0-D31 son los mismos que los registros VFPv3 D0-D31 y cada uno de los registros Q0-Q15 se asigna a un par de registros D. La Figura 1.3 muestra las diferentes vistas del banco de registros NEON y VFP compartido. Todas estas vistas son accesibles en cualquier momento. El software no tiene que cambiar explícitamente entre ellos, porque la instrucción utilizada determina la vista apropiada.

Los registros no compiten; más bien, coexisten como vistas del banco de registro. No hay manera de desenterrar el equipo NEON y FPU.

En relación a esto estoy usando las siguientes banderas de compilación:

-O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp -O3 -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp

Esto es lo que hago; Su experiencia puede ser diferente. Se deriva de un mashup de información recopilada de la plataforma y el compilador.

gnueabihf me dice que la plataforma usa flotadores duros, que pueden acelerar las llamadas de procedimiento. En caso de duda, use softfp porque es compatible con flotadores duros.

BeagleBone Black :

$ gcc -v 2>&1 | grep Target Target: arm-linux-gnueabihf $ cat /proc/cpuinfo model name : ARMv7 Processor rev 2 (v7l) Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32 ...

Así que el BeagleBone usa:

-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard

CubieTruck v5 :

$ gcc -v 2>&1 | grep Target Target: arm-linux-gnueabihf $ cat /proc/cpuinfo Processor : ARMv7 Processor rev 5 (v7l) Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4

Así que el CubieTruck usa:

-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard

Banana Pi Pro :

$ gcc -v 2>&1 | grep Target Target: arm-linux-gnueabihf $ cat /proc/cpuinfo Processor : ARMv7 Processor rev 4 (v7l) Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt

Entonces el Banana Pi usa:

-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard

Raspberry Pi 3 :

El RPI3 es único en su ARMv8, pero está ejecutando un sistema operativo de 32 bits. Eso significa que es efectivamente ARM de 32 bits o Aarch32. Hay un poco más de ARM contra Aarch32 de 32 bits, pero esto te mostrará las banderas de Aarch32

Además, el RPI3 utiliza un SoC Broadcom A53, tiene NEON y las instrucciones CRC32 opcionales, pero carece de las extensiones Crypto opcionales.

$ gcc -v 2>&1 | grep Target Target: arm-linux-gnueabihf $ cat /proc/cpuinfo model name : ARMv7 Processor rev 4 (v7l) Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 ...

Así que la Raspberry Pi puede usar:

-march=armv8-a+crc -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard

O puede usar (no sé qué usar para -mtune ):

-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard

ODROID C2 :

ODROID C2 utiliza un SoC A53 de Amlogic, pero utiliza un sistema operativo de 64 bits. El ODROID C2, tiene NEON y las instrucciones CRC32 opcionales, pero carece de las extensiones Crypto opcionales (configuración similar a RPI3).

$ gcc -v 2>&1 | grep Target Target: aarch64-linux-gnu $ cat /proc/cpuinfo Features : fp asimd evtstrm crc32

Entonces el ODROID usa:

-march=armv8-a+crc -mtune=cortex-a53

En las recetas anteriores, aprendí el procesador ARM (como Cortex A9 o A53) mediante la inspección de las hojas de datos. De acuerdo con esta respuesta en Unix y Linux Stack Exchange , que descifra el resultado de /proc/cpuinfo :

Pieza de la CPU: Número de pieza. 0xd03 indica el procesador Cortex-A53.

Por lo tanto, podemos buscar el valor desde una base de datos. No sé si existe o dónde se encuentra.

Estoy tratando de construir una biblioteca para un procesador ARM Cortex A9 (un OMAP4 para ser más específico) y estoy en un poco de confusión sobre qué / cuando usar NEON vs VFP en el contexto de operaciones de punto flotante y SIMD . Para señalar que conozco la diferencia entre las 2 unidades de coprocesador de hardware (como también se describe aquí en SO ), solo tengo algunos malentendidos con respecto a su uso adecuado.

En relación a esto estoy usando las siguientes banderas de compilación:

GCC -O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp -O3 -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp ARMCC --cpu=Cortex-A9 --apcs=/softfp --cpu=Cortex-A9 --fpu=VFPv3 --apcs=/softfp

He leído la documentación de ARM, una gran cantidad de artículos wiki ( como este ), foros y publicaciones de blog y todos parecen estar de acuerdo en que usar NEON es mejor que usar VFP o al menos mezclar NEON (por ejemplo, usar los instrumentos para implementar algunos algos) en SIMD) y VFP no es una buena idea; No estoy 100% seguro aún si esto se aplica en el contexto de toda la aplicación / biblioteca o simplemente a lugares específicos (funciones) en el código.

Así que estoy usando neon como la FPU para mi aplicación, ya que también quiero usar los intrínsecos. Como resultado, estoy en un pequeño problema y mi confusión sobre cómo usar mejor estas funciones (NEON vs VFP) en el Cortex A9 solo se profundiza aún más en lugar de aclarar. Tengo un código que hace evaluaciones comparativas para mi aplicación y utiliza algunas clases de temporizadores personalizados en los cuales los cálculos se basan en un punto flotante de doble precisión. El uso de NEON como FPU da resultados totalmente inapropiados (al imprimir esos valores se imprime principalmente inf y NaN; el mismo código funciona sin problemas cuando se construye para x86). Así que cambié mis cálculos para usar un punto flotante de precisión simple como se documenta que NEON no maneja el punto flotante de precisión doble . Mis puntos de referencia aún no dan los resultados correctos (y lo peor es que ahora ya no funciona en x86; creo que se debe a la pérdida de precisión, pero no estoy seguro). Así que estoy casi completamente perdido: por un lado, quiero usar NEON para las capacidades SIMD y usarlo ya que la FPU no proporciona los resultados adecuados, por otro lado, mezclarlo con el VFP no parece ser una buena idea. Cualquier consejo en esta área será muy apreciado!

Encontré en el artículo en el wiki mencionado anteriormente un resumen de lo que debería hacerse para la optimización de punto flotante en el contexto de NEON:

"

  • Utilice solo punto flotante de precisión simple
  • Utilice NEON intrinsics / ASM siempre que encuentre una función de FP de cuellos de botella. Puedes hacerlo mejor que el compilador.
  • Minimizar ramas condicionales
  • Habilitar el modo RunFast

Para softfp:

  • Código de punto flotante en línea (a menos que sea muy grande)
  • Pase argumentos de FP a través de punteros en lugar de por valor y realice un trabajo de enteros entre llamadas de función.

"

No puedo usar duro para el ABI flotante ya que no puedo enlazar con las bibliotecas que tengo disponibles. La mayoría de las recomendaciones tienen sentido para mí (excepto el "modo de ejecución" que no entiendo exactamente qué se supone que debe hacer y el hecho de que en este momento podría hacerlo mejor que el compilador), pero sigo obteniendo resultados inconsistentes y No estoy seguro de nada en este momento.

¿Alguien podría arrojar algo de luz sobre cómo usar correctamente el punto flotante y la NEON para el Cortex A9 / A8 y qué banderas de compilación debo usar?


Creo que esta pregunta debería dividirse en varias, agregando algunos ejemplos de código y detallando la plataforma de destino y las versiones de las cadenas de herramientas utilizadas.

Pero para cubrir una parte de la confusión: la recomendación de "usar NEON como FPU" parece un malentendido. NEON es un motor SIMD, el VFP es una FPU. Puede usar NEON para operaciones de punto flotante de precisión simple en hasta 4 valores de precisión simple en paralelo, lo que (cuando es posible) es bueno para el rendimiento.

-mfpu=neon puede verse como una abreviatura de -mfpu=neon-vfpv3 .

Consulte http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html para obtener más información.


Me mantendría alejado de VFP. Es igual que el modo Thmub: está destinado a compiladores. No tiene sentido optimizar para ellos.

Puede sonar grosero, pero realmente tampoco veo ningún punto en los intrínsecos de NEON. Es más problema que ayuda, si hay alguno.

Simplemente invierta dos o tres días en el ensamblaje ARM básico: solo necesita aprender algunas instrucciones para el control / terminación de bucle.

Luego, puede comenzar a escribir códigos NEON nativos sin preocuparse de que el compilador haga algo astral escupiendo toneladas de errores / advertencias.

Aprender las instrucciones de NEON es menos exigente que todas esas macros intrínsecas. Y por encima de todo esto, los resultados son mucho mejores.

Los códigos nativos de NEON completamente optimizados generalmente se ejecutan más del doble de rápido que las contrapartes intrínsecas bien escritas.

Solo compare la versión de OP con la mía en el enlace de abajo, luego sabrá a qué me refiero.

Optimización de la conversión de RGBA8888 a RGB565 con NEON

Saludos