Vectorización automática en doble y fast-math
-
27-09-2019 - |
Pregunta
¿Por qué es obligatorio usar -ffast-math
con g ++ para lograr la vectorización de bucles utilizando double
¿s? No me gusta -ffast-math
Porque no quiero perder precisión.
Solución
No necesariamente pierdes precisión con -ffast-math
. Solo afecta el manejo de NaN
, Inf
etc. y el orden en que se realizan las operaciones.
Si tiene un código específico en el que no desea que GCC reordene o simplifique los cálculos, puede marcar variables como utilizando un uso de un asm
declaración.
Por ejemplo, el siguiente código realiza una operación de redondeo en f
. Sin embargo, los dos f += g
y f -= g
Es probable que las operaciones sean optimizadas por GCC:
static double moo(double f, double g)
{
g *= 4503599627370496.0; // 2 ** 52
f += g;
f -= g;
return f;
}
En x86_64, puedes usar esto asm
Declaración para instruir a GCC que no realice esa optimización:
static double moo(double f, double g)
{
g *= 4503599627370496.0; // 2 ** 52
f += g;
__asm__("" : "+x" (f));
f -= g;
return f;
}
Desafortunadamente, deberá adaptar esto para cada arquitectura. En PowerPC, usa +f
en vez de +x
.
Otros consejos
Muy probable porque la vectorización significa que puede tener resultados diferentes, o puede significar que pierde las señales/excepciones de puntos flotantes.
Si está compilando para 32 bits X86, entonces GCC y G ++ predeterminan el uso del X87 para las matemáticas de punto flotante, en 64 bits predeterminados a SSE, sin embargo, el X87 puede y producirá diferentes valores para el mismo cálculo, por lo que es poco probable G ++ G ++ considerará la vectorización si no puede garantizar que obtendrá los mismos resultados a menos que use -ffast-math
o algunas de las banderas que enciende.
Básicamente se trata del entorno de punto flotante para el código vectorizado puede no ser el mismo que el de código no vectorizado, a veces de manera importante, si las diferencias no le importan, algo así como
-fno-math-errno -fno-trapping-math -fno-signaling-nans -fno-rounding-math
Pero primero busque esas opciones y asegúrese de que no afecten la corrección de su programa. -ffinite-math-only
puede ayudar también
Porque -ffast-math
habilitan operando reordenamiento lo que permite que muchos códigos se vectorizen.
Por ejemplo para calcular esto
sum = a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + … a[99]
El compilador es requerido Para hacer las adiciones secuencialmente sin que -ffast-math
, porque las matemáticas de punto flotante no son conmutadoras ni asociativas.
- ¿Es el conmutador de adición de punto flotante y asociativo?
- ¿Es el conmutador de adición de punto flotante en C ++?
- ¿Están asociadas las operaciones de puntos flotantes en C?
- ¿Es la adición de punto flotante y multiplicación asociativa?
Esa es la misma razón Por qué los compiladores no pueden optimizar a*a*a*a*a*a
a (a*a*a)*(a*a*a)
sin que -ffast-math
Eso significa que no hay vectorización disponible a menos que tenga agregadas muy eficientes del vector horizontal.
Sin embargo, si -ffast-math
está habilitado, la expresión se puede calcular como esto (Mirar A7. Auto-Vectorization
)
sum0 = a[0] + a[4] + a[ 8] + … a[96]
sum1 = a[1] + a[5] + a[ 9] + … a[97]
sum2 = a[2] + a[6] + a[10] + … a[98]
sum3 = a[3] + a[7] + a[11] + … a[99]
sum’ = sum0 + sum1 + sum2 + sum3
Ahora el compilador puede vectorizarlo fácilmente agregando cada columna en paralelo y luego hacer una suma horizontal al final
Lo hace
sum’ == sum
? Sólo si(a[0]+a[4]+…) + (a[1]+a[5]+…) + (a[2]+a[6]+…) + ([a[3]+a[7]+…) == a[0] + a[1] + a[2] + …
Esto se mantiene bajo asociatividad, que los flotadores no se adhieren, todo el tiempo. Especificación/fp:fast
Deje que el compilador transforme su código para que se ejecute más rápido, hasta 4 veces más rápido, para este cálculo simple.¿Prefieres rápido o preciso? - A7. Vectorización automática
Puede ser habilitado por el -fassociative-math
bandera en GCC