如何检查编译后的代码是否使用了SSE和AVX指令?

37

我编写了一些代码来进行一堆数学计算,需要快速执行,所以我需要它使用 SSE 和 AVX 指令。我使用 g++ 编译并加上 -O3-march=native 标志,因此我认为它正在使用 SSE 和 AVX 指令,但我不确定。我的大部分代码看起来像以下内容:

for(int i = 0;i<size;i++){
    a[i] = b[i] * c[i];
}

我怎样才能知道我的代码(编译后)是否使用了SSE和AVX指令?我认为我可以查看汇编代码来确定,但是我不懂汇编语言,也不知道如何查看编译器输出的汇编代码。


你可能也想使用向量扩展。 - Jester
4
使用命令g++ -S -o prog.s prog.cppGCC输出汇编代码。 - Galik
3
查看编译器输出: https://dev59.com/n1kT5IYBdhLWcg3wefUn。 @Galik: 显然,您需要使用g++ -march=native -O3 -S以获得带有优化的汇编输出。 还要注意的是,在标量FP代码中,您将看到SSE指令,例如vaddsd用于添加双精度数。 您要查找的是 vmulpd (打包双精度), vmulps (打包标量), 或者 vpmulld (整数打包添加双字(32位元素))或其他打包整数乘法指令,具体取决于bc的类型。 - Peter Cordes
这是一个非常常见的计算。请参阅std::inner_product。对于此,GPU可能会快几十倍。还要调查使用OMP。向量有多大? - Jive Dadson
@JiveDadson 这个问题比上面的例子要复杂一些,因为它涉及到表示张量的分步阵列。GPU的计算速度会更快,但是我对如何使用它一无所知,所以我先编写CPU代码。另外,我已经在使用OpenMP了。 - BadProgrammer99
如何在Linux上检查二进制文件是否需要SSE4或AVX? - phuclv
5个回答

47
在Linux下,您也可以反编译您的二进制文件:
objdump -d YOURFILE > YOURFILE.asm

然后找到所有的SSE指令:

awk '/[ \t](addps|addss|andnps|andps|cmpps|cmpss|comiss|cvtpi2ps|cvtps2pi|cvtsi2ss|cvtss2s|cvttps2pi|cvttss2si|divps|divss|ldmxcsr|maxps|maxss|minps|minss|movaps|movhlps|movhps|movlhps|movlps|movmskps|movntps|movss|movups|mulps|mulss|orps|rcpps|rcpss|rsqrtps|rsqrtss|shufps|sqrtps|sqrtss|stmxcsr|subps|subss|ucomiss|unpckhps|unpcklps|xorps|pavgb|pavgw|pextrw|pinsrw|pmaxsw|pmaxub|pminsw|pminub|pmovmskb|psadbw|pshufw)[ \t]/' YOURFILE.asm

仅查找打包的 SSE指令(由@Peter Cordes在评论中建议):

awk '/[ \t](addps|andnps|andps|cmpps|cvtpi2ps|cvtps2pi|cvttps2pi|divps|maxps|minps|movaps|movhlps|movhps|movlhps|movlps|movmskps|movntps|movntq|movups|mulps|orps|pavgb|pavgw|pextrw|pinsrw|pmaxsw|pmaxub|pminsw|pminub|pmovmskb|pmulhuw|psadbw|pshufw|rcpps|rsqrtps|shufps|sqrtps|subps|unpckhps|unpcklps|xorps)[ \t]/' YOURFILE.asm

查找所有SSE2指令(除了80386首次引入的MOVSD和CMPSD):

awk '/[ \t](addpd|addsd|andnpd|andpd|cmppd|comisd|cvtdq2pd|cvtdq2ps|cvtpd2dq|cvtpd2pi|cvtpd2ps|cvtpi2pd|cvtps2dq|cvtps2pd|cvtsd2si|cvtsd2ss|cvtsi2sd|cvtss2sd|cvttpd2dq|cvttpd2pi|cvtps2dq|cvttsd2si|divpd|divsd|maxpd|maxsd|minpd|minsd|movapd|movhpd|movlpd|movmskpd|movupd|mulpd|mulsd|orpd|shufpd|sqrtpd|sqrtsd|subpd|subsd|ucomisd|unpckhpd|unpcklpd|xorpd|movdq2q|movdqa|movdqu|movq2dq|paddq|pmuludq|pshufhw|pshuflw|pshufd|pslldq|psrldq|punpckhqdq|punpcklqdq)[ \t]/' YOURFILE.asm

仅查找打包的SSE2指令:

awk '/[ \t](addpd|andnpd|andpd|cmppd|cvtdq2pd|cvtdq2ps|cvtpd2dq|cvtpd2pi|cvtpd2ps|cvtpi2pd|cvtps2dq|cvtps2pd|cvttpd2dq|cvttpd2pi|cvttps2dq|divpd|maxpd|minpd|movapd|movapd|movhpd|movhpd|movlpd|movlpd|movmskpd|movntdq|movntpd|movupd|movupd|mulpd|orpd|pshufd|pshufhw|pshuflw|pslldq|psrldq|punpckhqdq|shufpd|sqrtpd|subpd|unpckhpd|unpcklpd|xorpd)[ \t]/' YOURFILE.asm

查找所有的SSE3指令:

awk '/[ \t](addsubpd|addsubps|haddpd|haddps|hsubpd|hsubps|movddup|movshdup|movsldup|lddqu|fisttp)[ \t]/' YOURFILE.asm

查找所有SSSE3指令:

awk '/[ \t](psignw|psignd|psignb|pshufb|pmulhrsw|pmaddubsw|phsubw|phsubsw|phsubd|phaddw|phaddsw|phaddd|palignr|pabsw|pabsd|pabsb)[ \t]/' YOURFILE.asm

查找所有SSE4指令:

awk '/[ \t](mpsadbw|phminposuw|pmulld|pmuldq|dpps|dppd|blendps|blendpd|blendvps|blendvpd|pblendvb|pblenddw|pminsb|pmaxsb|pminuw|pmaxuw|pminud|pmaxud|pminsd|pmaxsd|roundps|roundss|roundpd|roundsd|insertps|pinsrb|pinsrd|pinsrq|extractps|pextrb|pextrd|pextrw|pextrq|pmovsxbw|pmovzxbw|pmovsxbd|pmovzxbd|pmovsxbq|pmovzxbq|pmovsxwd|pmovzxwd|pmovsxwq|pmovzxwq|pmovsxdq|pmovzxdq|ptest|pcmpeqq|pcmpgtq|packusdw|pcmpestri|pcmpestrm|pcmpistri|pcmpistrm|crc32|popcnt|movntdqa|extrq|insertq|movntsd|movntss|lzcnt)[ \t]/' YOURFILE.asm

查找最常见的AVX指令(包括标量、AVX2、AVX-512系列以及一些类似于vfmadd132pd的FMA指令):

awk '/[ \t](vmovapd|vmulpd|vaddpd|vsubpd|vfmadd213pd|vfmadd231pd|vfmadd132pd|vmulsd|vaddsd|vmosd|vsubsd|vbroadcastss|vbroadcastsd|vblendpd|vshufpd|vroundpd|vroundsd|vxorpd|vfnmadd231pd|vfnmadd213pd|vfnmadd132pd|vandpd|vmaxpd|vmovmskpd|vcmppd|vpaddd|vbroadcastf128|vinsertf128|vextractf128|vfmsub231pd|vfmsub132pd|vfmsub213pd|vmaskmovps|vmaskmovpd|vpermilps|vpermilpd|vperm2f128|vzeroall|vzeroupper|vpbroadcastb|vpbroadcastw|vpbroadcastd|vpbroadcastq|vbroadcasti128|vinserti128|vextracti128|vpminud|vpmuludq|vgatherdpd|vgatherqpd|vgatherdps|vgatherqps|vpgatherdd|vpgatherdq|vpgatherqd|vpgatherqq|vpmaskmovd|vpmaskmovq|vpermps|vpermd|vpermpd|vpermq|vperm2i128|vpblendd|vpsllvd|vpsllvq|vpsrlvd|vpsrlvq|vpsravd|vblendmpd|vblendmps|vpblendmd|vpblendmq|vpblendmb|vpblendmw|vpcmpd|vpcmpud|vpcmpq|vpcmpuq|vpcmpb|vpcmpub|vpcmpw|vpcmpuw|vptestmd|vptestmq|vptestnmd|vptestnmq|vptestmb|vptestmw|vptestnmb|vptestnmw|vcompresspd|vcompressps|vpcompressd|vpcompressq|vexpandpd|vexpandps|vpexpandd|vpexpandq|vpermb|vpermw|vpermt2b|vpermt2w|vpermi2pd|vpermi2ps|vpermi2d|vpermi2q|vpermi2b|vpermi2w|vpermt2ps|vpermt2pd|vpermt2d|vpermt2q|vshuff32x4|vshuff64x2|vshuffi32x4|vshuffi64x2|vpmultishiftqb|vpternlogd|vpternlogq|vpmovqd|vpmovsqd|vpmovusqd|vpmovqw|vpmovsqw|vpmovusqw|vpmovqb|vpmovsqb|vpmovusqb|vpmovdw|vpmovsdw|vpmovusdw|vpmovdb|vpmovsdb|vpmovusdb|vpmovwb|vpmovswb|vpmovuswb|vcvtps2udq|vcvtpd2udq|vcvttps2udq|vcvttpd2udq|vcvtss2usi|vcvtsd2usi|vcvttss2usi|vcvttsd2usi|vcvtps2qq|vcvtpd2qq|vcvtps2uqq|vcvtpd2uqq|vcvttps2qq|vcvttpd2qq|vcvttps2uqq|vcvttpd2uqq|vcvtudq2ps|vcvtudq2pd|vcvtusi2ps|vcvtusi2pd|vcvtusi2sd|vcvtusi2ss|vcvtuqq2ps|vcvtuqq2pd|vcvtqq2pd|vcvtqq2ps|vgetexppd|vgetexpps|vgetexpsd|vgetexpss|vgetmantpd|vgetmantps|vgetmantsd|vgetmantss|vfixupimmpd|vfixupimmps|vfixupimmsd|vfixupimmss|vrcp14pd|vrcp14ps|vrcp14sd|vrcp14ss|vrndscaleps|vrndscalepd|vrndscaless|vrndscalesd|vrsqrt14pd|vrsqrt14ps|vrsqrt14sd|vrsqrt14ss|vscalefps|vscalefpd|vscalefss|vscalefsd|valignd|valignq|vdbpsadbw|vpabsq|vpmaxsq|vpmaxuq|vpminsq|vpminuq|vprold|vprolvd|vprolq|vprolvq|vprord|vprorvd|vprorq|vprorvq|vpscatterdd|vpscatterdq|vpscatterqd|vpscatterqq|vscatterdps|vscatterdpd|vscatterqps|vscatterqpd|vpconflictd|vpconflictq|vplzcntd|vplzcntq|vpbroadcastmb2q|vpbroadcastmw2d|vexp2pd|vexp2ps|vrcp28pd|vrcp28ps|vrcp28sd|vrcp28ss|vrsqrt28pd|vrsqrt28ps|vrsqrt28sd|vrsqrt28ss|vgatherpf0dps|vgatherpf0qps|vgatherpf0dpd|vgatherpf0qpd|vgatherpf1dps|vgatherpf1qps|vgatherpf1dpd|vgatherpf1qpd|vscatterpf0dps|vscatterpf0qps|vscatterpf0dpd|vscatterpf0qpd|vscatterpf1dps|vscatterpf1qps|vscatterpf1dpd|vscatterpf1qpd|vfpclassps|vfpclasspd|vfpclassss|vfpclasssd|vrangeps|vrangepd|vrangess|vrangesd|vreduceps|vreducepd|vreducess|vreducesd|vpmovm2d|vpmovm2q|vpmovm2b|vpmovm2w|vpmovd2m|vpmovq2m|vpmovb2m|vpmovw2m|vpmullq|vpmadd52luq|vpmadd52huq|v4fmaddps|v4fmaddss|v4fnmaddps|v4fnmaddss|vp4dpwssd|vp4dpwssds|vpdpbusd|vpdpbusds|vpdpwssd|vpdpwssds|vpcompressb|vpcompressw|vpexpandb|vpexpandw|vpshld|vpshldv|vpshrd|vpshrdv|vpopcntd|vpopcntq|vpopcntb|vpopcntw|vpshufbitqmb|gf2p8affineinvqb|gf2p8affineqb|gf2p8mulb|vpclmulqdq|vaesdec|vaesdeclast|vaesenc|vaesenclast)[ \t]/' YOURFILE.asm

注意:本文使用 gawknawk 进行测试。


2
你可能不想寻找标量SSE和SSE2指令;这个问题被标记为[simd],所以OP(以及大多数其他人)对普通的标量addss / addsd[u]comisd不感兴趣,只对addps / addpd / cmppd感兴趣。我已经在你在另一个问题上的第一个版本的答案中指出了这一点。(我知道这个gawk +正则表达式的东西看起来很熟悉,所以我把这个答案中的一个短语放到了谷歌中,找到了原始的答案。:)) - Peter Cordes
1
它需要快速运行,因此我需要它使用SSE和AVX指令。明确表明OP只是指自动向量化,可能没有意识到SSE2指令用于标量FP数学。如果您想在正则表达式中继续包含标量指令,则应在答案中明确说明,以便人们知道他们得到了什么。 - Peter Cordes
2
请注意,这些awk表达式对于mawk(Ubuntu 18.04上的默认awk)来说太长了:正则表达式/ [ \ t](addp ...超过了实现大小限制。gawk可以正常工作。 - bain
1
Mawk是ubuntu-minimal的一个依赖项。看起来自Ubuntu 16.04以来一直是这样,可能更早。 - bain
1
@PeterCordes在这里有一个objdump的分支,可以注释SSE2的使用情况。 - Noah
显示剩余10条评论

21

不需要检查汇编代码。大多数编译器都提供优化报告,准确地告诉您循环是否使用SIMD指令向量化。

如果使用GCC编译,请设置-O3 -march=native以确保使用任何SIMD指令集(SSE、AVX等)对向量化进行处理,并添加-fopt-info使编译器在优化方面冗长:

g++ -O3 -march=native -fopt-info -o main.o main.cpp

这将会产生如下输出:

main.cpp:12:20: note: loop vectorized
main.cpp:12:20: note: loop peeled for vectorization to enhance alignment

希望能帮到您。

2
请注意,大多数打包的SSE指令都以PS/PD结尾。在将二进制内容转储到asmfile后,我们将有一种更简单的方法来检查打包的SSEx指令。
grep %xmm asmfile | grep -P '([[:xdigit:]]{2}\s)+\s*[[:alnum:]]+p[sd]\s+'

或者xmm检查可以与模式结合使用。
grep -P '([[:xdigit:]]{2}\s)+\s*[[:alnum:]]+p[sd]\s+.+xmm' asmfile

这将足以满足仅使用浮点操作的程序。但是,为了更好的覆盖范围,您还需要检查以 P 开头的指令,因此您需要稍微更改一下正则表达式。

grep -P '([[:xdigit:]]{2}\s)+\s*([[:alnum:]]+p[sd]\s+|p[[:alnum:]]+).+%xmm' asmfile

要在32位代码中包含MMX指令,只需将末尾的%xmm部分更改为%x?mm
要检查AVX1/2,只需查找ymm%ymm的使用情况,而不是检查指令名称,因为AVX1/2指令只有矢量版本。
grep ymm asmfile

同样,AVX-512可以通过以下方式进行检查:

grep zmm asmfile

5
“ymm”短到足以出现在符号名称中。 objdump -d binary | egrep '%ymm[[:digit:]]+(,|$)' 可能更好。 利用AT&T语法装饰符号名称的“%”是避免误报的好方法。 检查后面是否跟着逗号或出现在行尾可能有点多余,因为我认为“%”不可能出现在符号名称中。 - Peter Cordes
@PeterCordes 或者你可以在传递给grep之前剥离二进制文件,如果它包含调试信息。 - phuclv
是的,但具有外部链接的函数名称仍将存在,并且可能还会存在全局变量作为操作数。(此外,您可能想知道哪些函数使用AVX指令,这种情况下您不应该进行剥离)。 - Peter Cordes
并非所有打包的SSE指令名称都以PS/PD结尾。例如:PMAXUB/PMAXUW,PAVGB/PAVGW,CVTPS2PI。链接 - Andriy Makukha
@AndriyMakukha 是的,但检查它们通常是不必要的。使用SIMD的程序很可能会有其他SIMD指令,比如add、mul...这就是为什么我甚至没有费心去检查load/store、conversion和许多shuffle指令,就像可以省略CMPSD一样。虽然如此,在MMX部分我还是加入了以P开头的指令检查。无论如何,我会更新我的答案。 - phuclv
这个回答有点暗示所有AVX-512指令都涉及512位向量。但它引入了一些适用于128或256位向量的新指令,例如vpternlogd,并且一些编译器在Skylake-avx512上默认使用256位进行自动向量化。如果您想检测512位向量使用情况,那么是的,请查找ZMM。否则(检测AVX512VL与较短向量,类似于标量或128位的vfmadd),这更难,尽管任何使用%k [0-9]的情况肯定是AVX512,如果有任何指令执行此操作。 - Peter Cordes

0
唯一的方法是反汇编生成的代码并查看它使用了哪些指令。
objdump -d <your executable or shared library>

5
这只是一个简单的部分,用谷歌5秒钟就能解决。难的部分是识别自动矢量化代码与标量代码,因为它们都使用相同的寄存器(至少对于标量FP而言)。 - Peter Cordes
@Peter Cordes:由编译器自动矢量化?这仍然需要生成可以轻松检查的汇编指令。最初的问题是如何确定所生成的汇编是否使用 SSE 或 AVX 指令。找到函数,查看指令即可。甚至不需要理解编译器优化就能够检查所生成的汇编中是否有针对所讨论函数的 SSE 或 AVX 指令。 - Michael Ngarimu
没错,但请注意问题中提到了性能。提问者可能没有意识到SSE或AVX将用于标量FP数学运算。所以他们真正想知道的与他们的问题标题不符。 - Peter Cordes

-4

正如其他人所指出的那样,您可以使用-S选项生成汇编代码。

此外,您还可以使用外部工具来反汇编已编译的二进制文件,例如objdump,或者更专业的ida。


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