c++ - FMA3 en GCC: cómo habilitar
intel avx (2)
Las siguientes opciones del compilador son suficientes para contratar _mm256_add_ps(_mm256_mul_ps(a, b), c)
a una sola instrucción fma ahora (por ejemplo, vfmadd213ps
):
GCC 5.3: -O2 -mavx2 -mfma
Clang 3.7: -O1 -mavx2 -mfma -ffp-contract=fast
ICC 13: -O1 -march=core-avx2
Probé /O2 /arch:AVX2 /fp:fast
con MSVC pero todavía no se contrae (sorpresa sorpresa). Sin embargo, MSVC contratará operaciones escalares .
GCC comenzó a hacer esto desde al menos GCC 5.1.
Tengo un i5-4250U que tiene AVX2 y FMA3. Estoy probando un código de multiplicación de matriz densa en GCC 4.8.1 en Linux que escribí. A continuación hay una lista de tres formas diferentes que compilo.
SSE2: gcc matrix.cpp -o matrix_gcc -O3 -msse2 -fopenmp
AVX: gcc matrix.cpp -o matrix_gcc -O3 -mavx -fopenmp
AVX2+FMA: gcc matrix.cpp -o matrix_gcc -O3 -march=native -fopenmp -ffast-math
Las versiones SSE2 y AVX son claramente diferentes en rendimiento. Sin embargo, el AVX2 + FMA no es mejor que la versión AVX. No entiendo esto Obtengo más del 80% de los fracasos máximos de la CPU suponiendo que no hay FMA pero creo que debería poder hacerlo mucho mejor con FMA. La multiplicación de matrices debe beneficiarse directamente de FMA. Básicamente estoy haciendo productos de ocho puntos a la vez en AVX. Cuando verifico march=native
da:
cc -march=native -E -v - </dev/null 2>&1 | grep cc1 | grep fma
...-march=core-avx2 -mavx -mavx2 -mfma -mno-fma4 -msse4.2 -msse4.1 ...
Así que puedo ver que está habilitado (solo para estar seguro de que agregué -mfma
pero no hace ninguna diferencia). ffast-math
debería permitir un modelo de punto flotante relajado Cómo utilizar las instrucciones de ffast-math
fusionada (FMA) con SSE / AVX
Editar:
Basándome en los comentarios de Mysticial, seguí adelante y usé _mm256_fmadd_ps y ahora la versión AVX2 + FMA es más rápida. No estoy seguro de por qué el compilador no hará esto por mí. Ahora estoy recibiendo alrededor de 80 GFLOPS (110% de los fracasos máximos sin FMA) para más de 1000x1000 matrices. En caso de que alguien no confíe en mi cálculo del fracaso máximo aquí es lo que hice.
peak flops (no FMA) = frequency * simd_width * ILP * cores
= 2.3GHZ * 8 * 2 * 2 = 73.2 GFLOPS
peak flops (with FMA) = 2 * peak flops (no FMA) = 146.2 GFLOPS
Mi CPU en modo turbo cuando uso ambos núcleos es de 2.3 GHz. Obtengo 2 para ILP porque Ivy Bridge puede hacer una multiplicación de AVX y una adición de AVX al mismo tiempo (y he desenrollado el bucle varias veces para asegurar esto).
Solo estoy obteniendo alrededor del 55% de los fracasos máximos (con FMA). No estoy seguro de por qué, pero al menos estoy viendo algo ahora.
Un efecto secundario es que ahora obtengo un pequeño error cuando me comparo con un algoritmo de multiplicación de matrices simple en el que sé que confío. Creo que eso se debe al hecho de que FMA solo tiene un modo de redondeo en lugar de lo que normalmente sería dos (lo que irónicamente rompe las reglas de punto flotante de IEEE, aunque probablemente sea mejor).
Editar:
Alguien tiene que rehacer ¿Cómo logro el máximo teórico de 4 FLOP por ciclo? pero haz 8 FLOPS de punto flotante doble por ciclo con Haswell.
Editar
En realidad, Mysticial ha actualizado su proyecto para apoyar a FMA3 (vea su respuesta en el enlace de arriba). Ejecuté su código en Windows8 con MSVC2012 (porque la versión de Linux no se compilaba con soporte de FMA). Aquí están los resultados.
Testing AVX Mul + Add:
Seconds = 22.7417
FP Ops = 768000000000
FLOPs = 3.37705e+010
sum = 17.8122
Testing FMA3 FMA:
Seconds = 22.1389
FP Ops = 1536000000000
FLOPs = 6.938e+010
sum = 333.309
Eso es 69.38 GFLOPS para FMA3 para punto flotante doble. Para el punto flotante único, necesito duplicarlo para que sea 138.76 SP GFLOPS. Calculo que mi pico es 146.2 SP GFLOPS. Eso es el 95% del pico! En otras palabras, debería poder mejorar mi código GEMM un poco (aunque ya es bastante más rápido que Eigen).
Sólo respondiendo una pequeña parte de la pregunta aquí. Si escribe _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0)
, gcc-4.9 lo maneja casi como asm en línea y no lo optimiza mucho. Si lo reemplaza con areg0*breg0+tmp0
, una sintaxis que es compatible con gcc y clang, gcc comienza a optimizar y puede usar FMA si está disponible. _mm256_add_ps
que para gcc-5, _mm256_add_ps
, por ejemplo, ahora se implementa como una función en línea que simplemente usa +
, por lo que el código con intrínsecos también se puede optimizar.