Comment détecter la disponibilité de SSE / SSE2 / AVX / AVX2 / AVX-512 / AVX-128-FMA / KCVI au moment de la compilation?

J’essaie d’optimiser certains calculs masortingciels et je me demandais s’il était possible de détecter à la compilation si SSE / SSE2 / AVX / AVX2 / AVX-512 / AVX-128-FMA / KCVI [1] est activé par le compilateur? Idéalement pour GCC et Clang, mais je ne peux gérer qu’avec un seul d’entre eux.

Je ne suis pas sûr que ce soit possible et peut-être que j’utiliserai ma propre macro, mais je préférerais la détecter plutôt et demander à l’utilisateur de la sélectionner.


[1] “KCVI” signifie optimisations de Knights Corner Vector Instruction. Les bibliothèques comme FFTW détectent / utilisent ces optimisations d’instructions plus récentes.

La plupart des compilateurs définiront automatiquement:

__SSE__ __SSE2__ __SSE3__ __AVX__ __AVX2__ 

etc, selon les commutateurs de ligne de commande que vous passez. Vous pouvez facilement vérifier cela avec gcc (ou les compilateurs compatibles gcc tels que clang), comme ceci:

 $ gcc -msse3 -dM -E - < /dev/null | egrep "SSE|AVX" | sort #define __SSE__ 1 #define __SSE2__ 1 #define __SSE2_MATH__ 1 #define __SSE3__ 1 #define __SSE_MATH__ 1 

ou:

 $ gcc -mavx2 -dM -E - < /dev/null | egrep "SSE|AVX" | sort #define __AVX__ 1 #define __AVX2__ 1 #define __SSE__ 1 #define __SSE2__ 1 #define __SSE2_MATH__ 1 #define __SSE3__ 1 #define __SSE4_1__ 1 #define __SSE4_2__ 1 #define __SSE_MATH__ 1 #define __SSSE3__ 1 

ou juste vérifier les macros prédéfinies pour une construction par défaut sur votre plate-forme particulière:

 $ gcc -dM -E - < /dev/null | egrep "SSE|AVX" | sort #define __SSE2_MATH__ 1 #define __SSE2__ 1 #define __SSE3__ 1 #define __SSE_MATH__ 1 #define __SSE__ 1 #define __SSSE3__ 1 

Les processeurs Intel plus récents prennent en charge AVX-512, qui n'est pas un jeu d'instructions monolithique. On peut voir le support disponible depuis GCC (version 6.2) pour deux exemples ci-dessous.

Voici Knights Landing:

 $ gcc -march=knl -dM -E - < /dev/null | egrep "SSE|AVX" | sort #define __AVX__ 1 #define __AVX2__ 1 #define __AVX512CD__ 1 #define __AVX512ER__ 1 #define __AVX512F__ 1 #define __AVX512PF__ 1 #define __SSE__ 1 #define __SSE2__ 1 #define __SSE2_MATH__ 1 #define __SSE3__ 1 #define __SSE4_1__ 1 #define __SSE4_2__ 1 #define __SSE_MATH__ 1 #define __SSSE3__ 1 

Voici Skylake AVX-512:

 $ gcc -march=skylake-avx512 -dM -E - < /dev/null | egrep "SSE|AVX" | sort #define __AVX__ 1 #define __AVX2__ 1 #define __AVX512BW__ 1 #define __AVX512CD__ 1 #define __AVX512DQ__ 1 #define __AVX512F__ 1 #define __AVX512VL__ 1 #define __SSE__ 1 #define __SSE2__ 1 #define __SSE2_MATH__ 1 #define __SSE3__ 1 #define __SSE4_1__ 1 #define __SSE4_2__ 1 #define __SSE_MATH__ 1 #define __SSSE3__ 1 

Intel a divulgué d'autres sous-ensembles AVX-512 (voir les extensions ISA ). GCC (version 7) prend en charge les indicateurs du compilateur et les symboles de préprocesseur associés aux sous-ensembles 4FMAPS, 4VNNIW, IFMA, VBMI et VPOPCNTDQ de AVX-512:

 for i in 4fmaps 4vnniw ifma vbmi vpopcntdq ; do echo "==== $i ====" ; gcc -mavx512$i -dM -E - < /dev/null | egrep "AVX512" | sort ; done ==== 4fmaps ==== #define __AVX5124FMAPS__ 1 #define __AVX512F__ 1 ==== 4vnniw ==== #define __AVX5124VNNIW__ 1 #define __AVX512F__ 1 ==== ifma ==== #define __AVX512F__ 1 #define __AVX512IFMA__ 1 ==== vbmi ==== #define __AVX512BW__ 1 #define __AVX512F__ 1 #define __AVX512VBMI__ 1 ==== vpopcntdq ==== #define __AVX512F__ 1 #define __AVX512VPOPCNTDQ__ 1