Alives 的回答已经提到了编译器的默认选项很保守。我列一下 Linux 发行版的默认选项,可以对保守程度有一个直观的认知:
如果是想确认能不能正常运行,一般情况下不需要识别。绝大多数软件二进制发布都是用的非常保守的指令集。少部分软件需要充分利用新指令集的优势,通常会编译多个版本的库,主程序启动时根据 cpuid 的结果来动态加载合适的库,新 CPU 可以充分发挥优势,旧 CPU 也能正常运行。至于必须要用新指令集才能运行的软件,都会在发行注记里面写清楚的。
当然根据 cpuid 动态加载也有可能会因为测试覆盖不全面出问题,尤其是游戏,很难通过虚拟机屏蔽指令集来测试。但这个也没有什么可靠的方法来识别(否则开发者自己识别了就不会出问题了)。比如某著名 MMORPG 最近才出过问题:
如果是想确认软件能不能发挥新指令集的优势,除了发行注记里面明确提到的,其它软件看看动态链接库的文件名基本上就能确认了。根据 cpuid 动态加载的,都会有类似
libxxx-generic.so
libxxx-avx.so
libxxx-avx2.so
这样的命名。
GCC/Clang
-march
和一大堆
-m
的选项(如
-msse3
-mavx
)来控制采用哪些指令集。(注意 GCC 的 x86 包括 x86-32、x86-64、x86-x32。)
最简单的方法就是直接用
-march=native
,让编译器自己根据 cpuid 开关选项。如果想知道
-march=native
开了哪些选项,执行
gcc -c -Q -march=native --help=target
MSVC 的控制没有这么精细,但也有
/arch:SSE
/arch:SSE2
/arch:AVX
/arch:AVX2
/arch:AVX512
这几个选项。