2016-05-31 67 views
1

我正在编译使用gcc -O2 -march = native标志的相同基准。然而,有趣的是,当我看着objdump时,它实际上产生了一些指令,如vxorpd等,我认为这应该只在启用了ftree-vectorize时出现(并且O2不应该默认启用此功能)?如果我添加了-m32标志在32位指令中编译时,这些打包的指令消失了。任何遇到类似情况的人都可以提供一些解释?谢谢。奇怪的gcc6.1 -O2编译行为

+0

通过一个具体的例子,你的问题会更容易回答。 –

回答

3

XORPD是传统的SSE2指令,它在两个压缩的双精度浮点值上执行按位逻辑异或。

VXORPD是该指令的矢量版本。实质上,它是带有VEX prefix的经典SSE2 XORPD指令。这就是操作码中“V”前缀的意思。它与AVX(高级矢量扩展)一起引入,并且在任何支持AVX的架构上都受支持。 (实际上有两个版本,适用于128位AVX寄存器的VEX.128编码版本和适用于256位AVX2寄存器的VEX.256编码版本。)

所有传统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指令。如果您启用基于矢量化的优化,则您更有可能仍然是。

+0

嗨科迪格雷,谢谢你的回复。还有一个问题需要跟进。在使用fastmath进行编译时,由于这些vex前缀说明,我看到了一个很大的加速。你知道这些vex-prefix指令如何比原来的x87指令表现得更好吗?谢谢 – PST

+0

@PST好的,常规的SSE指令往往比x87指令更快。这有多个复杂的原因。其中最重要的一点是x87 FPU可以在基于堆栈的系统中运行,并具有所有伴随的限制,而SSE实施使用寄存器。这意味着没有时间浪费在堆栈上压入/弹出值,或者在堆栈的不同位置交换值。 SSE比x87更快的另一个原因仅仅是它是一个更新的实现,并且已经相应地进行了优化。 –

+0

然后,我的回答已经解释了为什么以VEX为前缀的SSE指令比常规的SSE指令更快。因此,您将获得两项性能改进:首先从x87切换到SSE,然后从SSE切换到VEX编码的SSE。英特尔工程师必须在过去15 - 20年内达到某种程度。 :-) –

1

只需添加到Cody Gray's very good answer,您可以通过输出到汇编器并打开-fverbose-asm来检查gcc的内部启用选项。

例如:

gcc -O2 -fverbose-asm -S -o test.S test.c 

将列出test.S在选定的优化级别(这里-O2)启用所有的优化选项。

+0

另请参阅http://gcc.godbolt.org/,您可以在其中看到编译器asm输出并消除了噪声。 –