Evitar valores denormales en C ++

Después de buscar un error de rendimiento durante mucho tiempo, leí acerca de los valores de punto flotante denormal.

Los valores de punto flotante aparentemente desnormalizados pueden ser un gran problema de rendimiento, como se ilustra en esta pregunta: ¿Por qué el cambio de 0.1f a 0 disminuye el rendimiento en 10 veces?

Tengo un Intel Core 2 Duo y estoy comstackndo con gcc, usando -O2 .

¿Entonces qué hago? ¿Puedo de alguna manera instruir a g ++ para evitar valores denormales? Si no, ¿puedo de alguna manera probar si un float es desnormal?

Puedes probar si un flotador es destructivo usando

 #include  if ( std::fpclassify( flt ) == FP_SUBNORMAL ) 

(Advertencia: no estoy seguro de que esto se ejecute a toda velocidad en la práctica).

En C ++ 03, y este código me ha funcionado en la práctica,

 #include  #include  if ( flt != 0 && std::fabsf( flt ) < std::numeric_limits::min() ) { // it's denormalized } 

Para decidir dónde aplicar esto, puede usar un analizador basado en muestras como Shark, VTune o Zoom , para resaltar las instrucciones ralentizadas por valores negativos. La micro-optimización, incluso más que otras optimizaciones, es totalmente imposible sin análisis tanto antes como después.

Espere. Antes de hacer nada, ¿realmente sabe que su código está encontrando valores negativos y que está teniendo un impacto medible en el rendimiento?

Suponiendo que sepa eso, ¿sabe si el (los) algoritmo (s) que está (n) usando son estables si el soporte denormal está desactivado? Obtener la respuesta incorrecta 10 veces más rápido no suele ser una buena optimización del rendimiento.

Esas cuestiones a un lado:

  • Si desea detectar valores denormales para confirmar su presencia, tiene algunas opciones. Si tiene una biblioteca estándar C99 o Boost, puede usar la macro fpclassify . Alternativamente, puede comparar los valores absolutos de sus datos con el número normal positivo más pequeño.

  • Puede configurar el hardware para eliminar valores denormales a cero (FTZ) o tratar las entradas denormales como cero (DAZ). La manera más fácil, si es soportada apropiadamente en su plataforma, es probablemente usar la función fesetenv( ) en el encabezado C fenv.h Sin embargo, esta es una de las características menos compatibles del estándar C, y de todos modos es intrínsecamente específica de la plataforma. Puede usar simplemente un ensamblaje en línea para establecer directamente el estado de la FPU en (DAZ / FTZ).

La mayoría de los coprocesadores matemáticos tienen la opción de truncar valores denormales a cero. En x86 es el indicador FZ (Flush to Zero) en el registro de control MXCSR. Verifique su implementación de CRT para una función de soporte para establecer el registro de control. Debería estar en , algo parecido a _controlfp (). El bit de opción generalmente tiene “FLUSH” en el símbolo #defined.

Vuelva a verificar sus resultados matemáticos después de configurar esto. Lo cual es algo que deberías hacer de todos modos, obtener denormales es un signo de problemas de salud.

Tener FTZ (flush-to-zero) (asumiendo que underflow está enmascarado por defecto) en gcc:

 #define CSR_FLUSH_TO_ZERO (1 << 15) unsigned csr = __builtin_ia32_stmxcsr(); csr |= CSR_FLUSH_TO_ZERO; __builtin_ia32_ldmxcsr(csr); 

En caso de que no sea obvio por los nombres, __builtin_ia32_stmxcsr y __builtin_ia32_ldmxcsr solo están disponibles si se dirige a un procesador x86. ARM, Sparc, MIPS, etc. necesitarán cada uno de los códigos específicos de la plataforma con este enfoque.

Solo como una adición a las otras respuestas, si realmente tiene un problema con los valores de coma flotante denormal probablemente tenga un problema de precisión además de su problema de rendimiento.

Puede ser una buena idea verificar si puede reestructurar sus cálculos para mantener los números más grandes para evitar perder precisión y rendimiento.

Al parecer, quiere algunas instrucciones de CPU llamadas FTZ (Flush To Zero) y DAZ (Denormals Are Zero).

Encontré la información en un sitio web de audio pero faltaba su enlace a la documentación de Intel. Aparentemente son instrucciones SSE2, por lo que deberían funcionar con CPU AMD que lo respalden.

No sé qué puedes hacer en GCC para forzarlo de una manera portátil. Sin embargo, siempre puede escribir código ensamblador en línea para usarlos. Puede que tenga que forzar a GCC a usar solo SSE2 para matemáticas de coma flotante.