如何在编译时检测SSE/SSE2/AVX/AVX2/AVX-512/AVX-128-FMA/KCVI的可用性?

85
我正在尝试优化一些矩阵计算,想知道是否能够在编译时检测到编译器是否启用了 SSE/SSE2/AVX/AVX2/AVX-512/AVX-128-FMA/KCVI[1]。最好是针对 GCC 和 Clang,但如果只能使用其中一个也可以。我不确定这是否可能,也许我会使用自己的宏,但我更喜欢检测它,而不是要求用户进行选择。
[1] "KCVI" 代表 Knights Corner 向量指令优化。像 FFTW 这样的库可以检测和利用这些较新的指令优化。

4
你想测试什么?你想测试编译器是否能生成AVX指令吗?需要注意的是,仅因为编译器准备好生成指令并不意味着你的程序最终运行的CPU也支持它(即使编译和执行都在同一台机器上进行)。 - ArjunShankar
1
@ArjunShankar 我想知道在编译时是否使用了 -mavx 来启用 avx。 - Baptiste Wicht
4
注意,CPU支持和操作系统支持是两个不同的事情。CPU可能支持SSE指令集,但操作系统可能不支持SSE(这要求操作系统在上下文切换期间保存XMM寄存器)。例如,请参见OSDev Wiki上的“检查SSE”页面。 - jww
2个回答

125

大多数编译器将自动定义:

__SSE__
__SSE2__
__SSE3__
__AVX__
__AVX2__

根据您传递的任何命令行开关,等等。您可以使用gcc(或gcc兼容的编译器,如clang)轻松检查此内容,方法如下:

$ 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

或者:
$ 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

或者只需检查针对您特定平台的默认构建的预定义宏:

$ 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

较新的英特尔处理器支持AVX-512,这不是一个单olithic的指令集。您可以看到GCC(版本6.2)支持以下两个示例。

这里是骑士着陆:

$ 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

这里是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

英特尔已经披露了额外的AVX-512子集(请参见ISA扩展)。GCC(7版本)支持与AVX-512的4FMAPS,4VNNIW,IFMA,VBMI和VPOPCNTDQ子集相关的编译器标志和预处理器符号:

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

请注意,SSE宏在Visual C++中不起作用。您必须使用 _M_IX86_FP


2
请注意,SSE宏不适用于Visual C ++。您必须使用_M_IX86_FP:https://msdn.microsoft.com/en-us/library/b0084kay.aspx - Rémi
2
@Rémi:是的,很典型 - 如果你被迫支持MSVC,最简单的方法就是在你的项目或makefile中定义SSE宏。 - Paul R
3
我认为最后一个需要添加"-march=native"参数……值得注意的是:GCC定义了各个AVX512子集(例如__AVX512F____AVX512BW__)。 - ZachB
@ZachB:我认为这取决于你的gcc安装,但上面的输出是直接从我在2015年安装的任何工具链中获取的。如果我尝试使用“-march=native”来运行最后一个命令,我会得到与“-mavx2”相同的结果(在Haswell机器上)。感谢您提供有关AVX512的信息 - 我会及时更新答案... - Paul R
1
@PaulR,希望你不介意,我添加了所有公开记录的AVX-512信息。#IamIntel - Jeff Hammond
1
对于最新的MacPro 2019,它采用了cascadelake而不是skylake-avx512,并添加了AVX512VNNI。 - Stephane

1

2
看起来不错,但问题是关于编译时检查的。(顺便说一句,这不是我的投票。) - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接