【问题标题】:Strange gcc6.1 -O2 compiling behaviour奇怪的 gcc6.1 -O2 编译行为
【发布时间】:2016-09-29 16:14:35
【问题描述】:

我正在使用 gcc -O2 -march=native 标志编译相同的基准。然而,有趣的是,当我查看objdump 时,它实际上会产生一些指令,如vxorpd 等,我认为这些指令只应在启用-ftree-vectorize 时出现(而-O2 默认不应该启用它? ) 如果我在 32 位指令中添加 -m32 标志来编译,这些打包指令就会消失。有遇到过类似情况的能解释一下吗?谢谢。

【问题讨论】:

  • 用一个具体的例子来回答你的问题会更容易。

标签: gcc optimization x86 x86-64 compiler-optimization


【解决方案1】:

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

这就解释了为什么在使用 -march=native 开关编译 64 位版本时会看到 VXORPD 及其朋友。

这就留下了一个问题,为什么当您抛出-m32 开关(这意味着为32 位平台生成代码)时您没有看到它。 SSE 和 AVX 指令在针对这些平台时仍然可用,并且我相信它们会在某些情况下使用,但由于 32 位 ABI 的显着差异,它们不能被频繁使用。具体来说,32 位 ABI 要求在 x87 浮点堆栈上返回浮点值。由于这需要使用 x87 浮点指令,因此优化器倾向于坚持使用这些指令,除非它对一段代码进行大量矢量化。这是唯一一次真正有意义地将 x87 堆栈中的值混洗到 SIMD 寄存器并再次返回。否则,这将是性能消耗,几乎没有实际好处。

您也可以看到这一点。只需抛出-m32 开关即可查看输出的变化:

Foo():
        fldz
        ret

FLDZ 是 x87 FPU 指令,用于在浮点堆栈顶部加载常量零,准备好返回给调用者。

显然,当您使代码更复杂时,您更有可能更改优化器的启发式方法并说服它发出 SIMD 指令。如果启用基于矢量化的优化,则更有可能。

【讨论】:

  • 嗨科迪格雷,感谢您的回复。还有一个问题要跟进。在使用 fastmath 编译时,由于这些 vex 前缀指令,我看到了很大的加速。您知道这些 vex-prefix 指令如何比原始 x87 指令执行得更好吗?谢谢
  • @PST 好吧,常规的 SSE 指令往往比 x87 指令快。这有多种复杂的原因。其中最重要的一点是 x87 FPU 在基于堆栈的系统上工作,具有所有伴随的限制,而 SSE 实现使用寄存器。这意味着不会浪费时间在堆栈上推送/弹出值,或在堆栈上的不同位置交换值。 SSE 比 x87 更快的另一个原因是它是一个更新的实现,并且已经相应地进行了优化。
  • 那么,我的回答已经解释了为什么以 VEX 为前缀的 SSE 指令比常规的 SSE 指令更快。因此,您将受益于两项性能改进:首先从 x87 切换到 SSE,然后从 SSE 切换到 VEX 编码的 SSE。英特尔工程师必须在过去 15 到 20 年中有所作为。 :-)
【解决方案2】:

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

例如:

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

将在test.S 中列出在所选优化级别启用的所有优化选项(此处为-O2)。

【讨论】:

  • 另请参阅gcc.godbolt.org,您可以在其中看到编译器 asm 输出的噪音已被去除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多