我正在使用gcc -O2 -march=native
标志编译相同的基准。但是,有趣的是,当我查看objdump
时,它实际上会生成一些指令,例如vxorpd
等,我认为只有在启用-ftree-vectorize
时才会出现(-O2
默认情况下不应该启用它?)如果我在32位指令中添加-m32
标志进行编译,这些打包指令就会消失。遇到类似情况的人可以给出一些解释吗?感谢。
答案 0 :(得分:3)
XORPD
是经典的SSE2指令,它对两个压缩的双精度浮点值执行按位逻辑XOR。
VXORPD
是同一指令的矢量版本。从本质上讲,它是带有VEX prefix的经典SSE2 XORPD
指令。这就是“V”前缀在操作码中的含义。它是随AVX(高级矢量扩展)引入的,并且在任何支持AVX的架构上都受支持。 (实际上有两个版本,VEX.128编码版本适用于128位AVX寄存器,VEX.256编码版本适用于256位AVX2寄存器。)
所有旧版SSE和SSE2指令都可以添加一个VEX前缀,为它们提供三操作数形式,并允许它们与其他新AVX指令进行交互和更有效地安排。它也避免了the high cost of transitions between VEX and non-VEX modes。否则,这些新编码保持相同的行为。因此,只要目标体系结构支持这些指令,编译器通常就会生成这些指令的VEX前缀版本。显然,在您的情况下,march=native
指定的架构至少支持AVX。
在GCC和Clang上,即使关闭了优化(-O0
),您实际上也会发出这些指令,所以在启用优化时你肯定会得到它们。 -ftree-vectorize
开关或任何其他向量化特定的优化开关都不需要打开,因为这实际上与向量化代码没有任何关系。更确切地说,代码流没有改变,只是指令的编码。
你可以用最简单的代码看到这个:
double Foo()
{
return 0.0;
}
Foo():
vxorpd xmm0, xmm0, xmm0
ret
这就解释了为什么当您使用VXORPD
开关编译64位版本时,您会看到-march=native
及其朋友。
这就留下了为什么当你抛出-m32
开关时不看到它的原因(这意味着为32位平台生成代码)。当针对这些平台时,SSE和AVX指令仍然可用,我相信它们将在某些情况下使用,但由于32位ABI的显着差异,它们不能经常使用。具体来说,32位ABI要求在x87浮点堆栈上返回浮点值。由于这需要使用x87浮点指令,因此优化器倾向于坚持使用这些指令,除非它对一段代码进行大量矢量化。这是将x87堆栈中的值重新混合到SIMD寄存器并再次返回的唯一时间。否则,这是一种性能消耗,几乎没有实际效益。
你也可以看到这一点。通过抛出-m32
开关来查看输出中的变化:
Foo():
fldz
ret
FLDZ
是用于在浮点堆栈顶部加载常量零的x87 FPU指令,可以将其返回给调用者。
显然,当您使代码更复杂时,您更有可能更改优化程序的启发式方法并说服它发出SIMD指令。如果启用基于矢量化的优化,则更有可能。
答案 1 :(得分:1)
只需添加到Cody Gray's very good answer,您就可以通过输出到汇编程序并启用-fverbose-asm
来检查gcc的内部启用选项。
例如:
gcc -O2 -fverbose-asm -S -o test.S test.c
将在test.S
列出所选优化级别启用的所有优化选项(此处为-O2
)。