Modos de suma de multiplicación fusionada y redondeo predeterminado

7 minutos de lectura

avatar de usuario
bosón z

Con GCC 5.3 el siguiente código se compila con -O3 -fma

float mul_add(float a, float b, float c) {
  return a*b + c;
}

produce el siguiente ensamblaje

vfmadd132ss     %xmm1, %xmm2, %xmm0
ret

Noté que GCC hacía esto con -O3 ya en GCC 4.8.

Sonido 3.7 con -O3 -mfma produce

vmulss  %xmm1, %xmm0, %xmm0
vaddss  %xmm2, %xmm0, %xmm0
retq

pero Clang 3.7 con -Ofast -mfma produce el mismo código que GCC con -O3 fast.

Me sorprende que GCC haga con -O3 porque de esta respuesta dice

El compilador no puede fusionar sumas y multiplicaciones separadas a menos que permita un modelo relajado de punto flotante.

Esto se debe a que un FMA solo tiene un redondeo, mientras que un ADD + MUL tiene dos. Por lo tanto, el compilador violará el estricto comportamiento de punto flotante IEEE al fusionarse.

Sin embargo, desde este enlace dice

Independientemente del valor de FLT_EVAL_METHOD, se puede contraer cualquier expresión de coma flotante, es decir, se puede calcular como si todos los resultados intermedios tuvieran un rango y una precisión infinitos.

Así que ahora estoy confundido y preocupado.

  1. ¿Se justifica que GCC use FMA con -O3?
  2. ¿La fusión viola el estricto comportamiento de punto flotante IEEE?
  3. Si la fusión viola el comportamiento de coma flotante de IEEE y dado que GCC regresa __STDC_IEC_559__ ¿No es esto una contradicción?

Dado que FMA se puede emular en el software, parece que debería haber dos conmutadores de compilador para FMA: uno para decirle al compilador que use FMA en los cálculos y otro para decirle al compilador que el hardware tiene FMA.


Apprently esto se puede controlar con la opción -ffp-contract. Con GCC el valor predeterminado es -ffp-contract=fast y con Clang no lo es. Otras opciones como -ffp-contract=on y -ffp-contract=off no produzca la instrucción FMA.

Por ejemplo Clang 3.7 con -O3 -mfma -ffp-contract=fast produce vfmadd132ss.


Revisé algunas permutaciones de #pragma STDC FP_CONTRACT ajustado a ON y OFF con -ffp-contract ajustado a on, offy fast. EN todos los casos también usé -O3 -mfma.

Con GCC la respuesta es simple. #pragma STDC FP_CONTRACT ENCENDIDO o APAGADO no hace ninguna diferencia. Solamente -ffp-contract asuntos.

CCG que utiliza fma con

  1. -ffp-contract=fast (por defecto).

Con Clang usa fma

  1. con -ffp-contract=fast.
  2. con -ffp-contract=on (predeterminado) y #pragma STDC FP_CONTRACT ON (el valor predeterminado es OFF).

En otras palabras, con Clang puedes obtener fma con #pragma STDC FP_CONTRACT ON (ya que -ffp-contract=on es el predeterminado) o con -ffp-contract=fast. -ffast-math (y por lo tanto -Ofast) colocar -ffp-contract=fast.


Busqué en MSVC e ICC.

Con MSVC usa la instrucción fma con /O2 /arch:AVX2 /fp:fast. Con MSVC /fp:precise es el predeterminado.

Con ICC usa fma con -O3 -march=core-avx2 (en realidad -O1 es suficiente). Esto se debe a que, por defecto, ICC usa -fp-model fast. Pero ICC usa fma incluso con -fp-model precise. Para deshabilitar fma con el uso de ICC -fp-model strict o -no-fma.

Entonces, por defecto, GCC e ICC usan fma cuando fma está habilitado (con -mfma para GCC/Clang o -march=core-avx2 con ICC), pero Clang y MSVC no.

  • Podría ser un error del compilador. Considere denunciarlo.

    – fuz

    23 de diciembre de 2015 a las 13:01

  • Estoy bastante seguro de que lo que está haciendo gcc está bien. Después de leer el documento FLT_EVAL_METHOD sobre la contratación de expresiones FP, estoy sorprendido clang no hacer esto. No estoy publicando esto como una respuesta, ya que no se basa en ninguna documentación de estándares reales, solo en mi comprensión de cómo I creo que las cosas deberían funcionar/deberían haber sido diseñadas, dado el material de la pregunta.

    – Peter Cordes

    23 de diciembre de 2015 a las 13:04

  • @FUZxxl, ¿cree que la etiqueta de punto flotante sería más apropiada que ieee-754? (si es así, siéntete libre de cambiarlo). Siento que también debería usar la etiqueta de punto flotante.

    – bosón Z

    23 de diciembre de 2015 a las 13:06


  • “¿La fusión viola el estricto comportamiento de punto flotante IEEE?” –> OMI, sí. Utilizar double fma(double x, double y, double z);en cambio, ya que es una llamada de función que en un compilador optimizado llamará al código ensamblador esperado. Esto no viola el “comportamiento de punto flotante IEEE”.

    – chux – Reincorporar a Monica

    23 de diciembre de 2015 a las 13:23


  • gcc.gnu.org/bugzilla/show_bug.cgi?id=37845

    – bosón Z

    23 de diciembre de 2015 a las 14:21

No viola IEEE-754, porque IEEE-754 difiere a los idiomas en este punto:

Un estándar de lenguaje también debe definir, y requerir implementaciones para proporcionar, atributos que permitan y no permitan optimizaciones de cambio de valor, por separado o colectivamente, para un bloque. Estas optimizaciones pueden incluir, entre otras:

― Síntesis de una operación fusedMultiplyAdd a partir de una multiplicación y una suma.

En el estándar C, el STDC FP_CONTRACT pragma proporciona los medios para controlar esta optimización de cambio de valor. Por lo tanto, GCC tiene licencia para realizar la fusión de forma predeterminada, siempre que le permita desactivar la optimización configurando STDC FP_CONTRACT OFF. No apoyar eso significa no adherirse al estándar C.

  • ¿Qué quiere decir con “No admitir eso significa no adherirse al estándar C”? Por cierto, GCC parece ignorar STDC FP_CONTRACT. En cambio, solo usa -ffp-contract. Clang reconoce a ambos.

    – bosón Z

    16 de enero de 2016 a las 18:58


  • Quiero decir que FP_CONTRACT es parte del estándar C. Ignorarlo es no conformarse.

    – Esteban Canon

    16 de enero de 2016 a las 19:38

  • Oh, me di cuenta de que te referías a que GCC no es compatible FP_CONTRACT (o cualquier compilador que no lo admita). Ahora entiendo.

    – bosón Z

    16/01/2016 a las 20:05


  • Entonces, ¿esta respuesta es incorrecta, entonces “el compilador violará el comportamiento estricto de punto flotante IEEE al fusionarse”? Eso es lo que a través de mí fuera.

    – bosón Z

    16 de enero de 2016 a las 20:08


  • El valor predeterminado puede ser ON u OFF. Pero necesita apoyar el pragma para cumplir con el estándar.

    – Esteban Canon

    16 de enero de 2016 a las 20:24

Cuando citó que se permite la suma y multiplicación fusionada, omitió la condición importante “a menos que pragma FP_CONTRACT esté desactivado”. Que es una característica nueva en C (creo que se introdujo en C99) y PowerPC la hizo absolutamente necesaria, lo que todos había fusionado multiplicar y sumar desde el principio; en realidad, x*y era equivalente a fma (x, y, 0) y x+y era equivalente a fma (1.0, x, y).

FP_CONTRACT es lo que controla la multiplicación/suma fusionada, no FLT_EVAL_METHOD. Aunque si FLT_EVAL_METHOD permite mayor precisión, entonces la contratación siempre es legal; solo finja que las operaciones se realizaron con una precisión muy alta y luego se redondearon.

La función fma es útil si no quieres la velocidad, sino la precisión. Calculará el resultado contratado de forma lenta pero correcta aunque no esté disponible en hardware. Y debe estar en línea si está disponible en hardware.

  • Creo que esto responde hasta cierto punto a mi primera pregunta sobre si GCC se justifica solo en fma con -O3. Pero aún no está claro si es compatible con IEEE. Y dado que GCC define __STDC_IEC_559__ entonces puedo asumir que es compatible con IEEE pero otras personas afirman que fma rompe esto (lo que argumentaría que GCC no está justificado para hacer esto cuando __STDC_IEC_559__ se define). Así que todavía estoy confundido.

    – bosón Z

    23 de diciembre de 2015 a las 20:04


  • @Zboson: noté esas cosas sobre el pragma en el documento que te vinculé, pero no sabía qué tan nuevo o ampliamente respaldado era. Por eso no lo mencioné antes.

    – Peter Cordes

    23 de diciembre de 2015 a las 23:08

  • @PeterCordes, está bien, a GCC no parece importarle ese pragma de todos modos, por lo que es un problema discutible. Y en cualquier caso, no dice nada acerca de que sea compatible con IEEE. Devoluciones del CCG __STDC_IEC_559__ y en los mismos usos -ffp-contract=fast así que todavía quiero saber si esto es una contradicción.

    – bosón Z

    24 de diciembre de 2015 a las 14:07

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad