【问题标题】:How to check if compiled code uses SSE and AVX instructions?如何检查编译后的代码是否使用 SSE 和 AVX 指令?
【发布时间】:2018-06-01 09:06:43
【问题描述】:

我写了一些代码来做一堆数学运算,它需要快速运行,所以我需要它来使用 SSE 和 AVX 指令。我正在使用带有标志-O3-march=native 的g++ 编译它,所以我认为它使用的是SSE 和AVX 指令,但我不确定。我的大部分代码如下所示:

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

有什么方法可以判断我的代码(编译后)是否使用 SSE 和 AVX 指令?我想我可以看一下程序集,但是我不知道程序集,也不知道如何查看编译器输出的程序集。

【问题讨论】:

  • 您可能也想使用矢量扩展。
  • 获取GCC输出汇编器g++ -S -o prog.s prog.cpp
  • 查看编译器输出:stackoverflow.com/questions/38552116/…。 @Galik:显然你必须使用g++ -march=native -O3 -S 来获得优化的asm 输出。另请注意,您将在标量 FP 代码中看到 SSE 指令,例如 vaddsd 添加双精度。您正在寻找 vmulpd(压缩双精度)、vmulps(压缩标量)或 vpmulld(整数压缩加 dword(32 位元素)或其他压缩整数乘法指​​令,具体取决于 @987654332 的类型@ 和 c.
  • 这是一个很常见的计算。见 std::inner_product。 GPU 可能会快几十倍。还使用 OMP 进行调查。向量有多大?
  • @JiveDadson 比上面的例子复杂一点,因为它是在一个跨步数组上,代表一个张量。 GPU 会跑得更快,但我对使用它一无所知,所以我要先编写 CPU 代码。另外,我已经在使用 OpenMP。

标签: c++ assembly x86 g++ simd


【解决方案1】:

无需检查组件。大多数编译器都会提供优化报告,准确地告诉您您的循环是否使用 SIMD 指令进行了矢量化。

如果您使用 GCC 编译,请设置 -O3 -march=native 以确保使用您正在编译的 CPU 支持的任何 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】:

    唯一的判断方法是反汇编生成的代码,看看它使用了什么指令。

    objdump -d <your executable or shared library>
    

    【讨论】:

    • 这只是简单的部分,用 google 5 秒可以解决。困难的部分是识别自动矢量化代码与标量,因为它们都使用相同的寄存器(至少对于标量 FP)。
    • @Peter Cordes:由编译器自动矢量化?这仍然必须产生易于检查的汇编指令。最初的问题询问如何判断生成的程序集是使用 SSE 还是 AVX 指令。找到函数,看说明。甚至不需要了解编译器优化来检查生成的指令并查看为相关函数生成的程序集中是否有任何 SSE 或 AVX 指令。
    • 没错,但请注意问题中提到了性能。问的人可能没有意识到 SSE 或 AVX 将用于标量 FP 数学。所以他们真正想知道的与他们的问题标题不符。
    【解决方案3】:

    请注意,大多数打包的 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
    

    【讨论】:

    • ymm 足够短,可以出现在符号名称中。 objdump -d binary | egrep '%ymm[[:digit:]]+(,|$)' 可能会更好。利用% 的AT&T 语法修饰是避免误报的好方法。检查后跟逗号或出现在行尾可能有点矫枉过正,因为我认为% 不能出现在符号名称中。
    • @PeterCordes 或者如果二进制文件包含调试信息,您也可以在传递给 grep 之前剥离二进制文件
    • 是的,但是具有外部链接的函数名称仍然存在,并且可能还有全局变量作为操作数。 (另外,您可能想知道 哪些 函数使用 AVX 指令,在这种情况下您不想剥离)。
    • 并非所有打包的 SSE 指令名称都以 PS/PD 结尾。例如:PMAXUB/PMAXUWPAVGB/PAVGWCVTPS2PI
    • @AndriyMakukha 是的,但检查这些通常是不必要的。使用 SIMD 的程序很可能会有其他 SIMD 指令,如 add、mul ......这就是为什么我什至不费心检查加载/存储、转换和许多 shuffle 指令的原因,就像可以省略 CMPSD 一样。不过,我在 MMX 部分中检查了以 P 开头的指令。无论如何,我会更新我的答案
    【解决方案4】:

    在 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
    

    仅查找 packed SSE 指令(由 cmets 中的 @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 指令(MOVSD 和 CMPSD 除外,它们在 80386 中首次引入):

    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 系列和一些 FMA,如 vfmadd132pd):

    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 进行测试。

    【讨论】:

    • 您可能不想寻找标量 SSE 和 SSE2 指令;这个问题被标记为[simd],所以OP(和大多数其他人)对普通标量addss/addsd[u]comisd不感兴趣,只有addps/addpd/cmppd。我已经指出了这一点on your first version of this answer on another question。 (我知道这个gawk + regex 看起来很熟悉,所以我把这个答案中的一个短语放到谷歌中并找到了原文。:)
    • @PeterCordes,谢谢 :) 当我试图找到我的旧答案时,我刚刚发现了这个问题,所以决定也在这里发布这个答案,但由于您之前的评论而略有更新。至于 SIMD/标量的区别,您的建议在某些情况下可能是好的,但这里的问题并没有明确地将兴趣仅限于 SIMD 指令。 [simd] 标签可用于引起熟悉 SSE 和 AVX 指令的人的注意。
    • 它需要快,所以我需要它来使用 SSE 和 AVX 指令。 明确指出 OP 仅表示自动矢量化,可能没有意识到 SSE2指令用于标量 FP 数学。如果你想在你的正则表达式中继续包含标量指令,你应该在你的答案中明确说明,这样人们就知道他们得到了什么。
    • 请注意,这些 awk 表达式对于 mawk(Ubuntu 18.04 上的默认 awk)来说太长了:regular expression /[ \t](addp ... exceeds implementation size limit。 gawk 很好。
    • Mawk 是 ubuntu-minimal 的一个依赖。看起来从at least Ubuntu 16.04 开始就是这样了,可能更早。
    【解决方案5】:

    正如其他人指出的,您可以使用 -S 来生成汇编代码。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-20
      • 2013-06-11
      • 2016-04-15
      • 2019-01-17
      • 2017-05-08
      相关资源
      最近更新 更多