为什么这一系列指令更快?

时间:2018-01-16 13:22:21

标签: c++ gcc assembly x86 clang

我正在比较GCC和Clang输出以评估浮点表达式,并偶然发现了我无法解释的性能差异。

源代码

float evaluate(float a, float b) {
    return (a - b + 1.0f) * (a - b) * (a - b - 1.0f);
}

GCC 7.2(-std = c ++ 1y -O2)产生

evaluate(float, float):
  subss xmm0, xmm1
  movss xmm2, DWORD PTR .LC0[rip]
  movaps xmm1, xmm0
  addss xmm1, xmm2
  mulss xmm1, xmm0
  subss xmm0, xmm2
  mulss xmm0, xmm1
  ret
.LC0:
  .long 1065353216

虽然Clang 5.0.0(-std = c ++ 1y -O2)产生了

.LCPI0_0:
  .long 1065353216 # float 1
.LCPI0_1:
  .long 3212836864 # float -1
evaluate(float, float): # @evaluate(float, float)
  subss xmm0, xmm1
  movss xmm1, dword ptr [rip + .LCPI0_0] # xmm1 = mem[0],zero,zero,zero
  addss xmm1, xmm0
  mulss xmm1, xmm0
  addss xmm0, dword ptr [rip + .LCPI0_1]
  mulss xmm0, xmm1
  ret

GCC似乎更喜欢movaps而不是movss,即使在这种情况下movss就足够了。正如Peter Cordes所指出的,这实际上更好,因为使用movaps可以避免因对XMM寄存器的部分更新而失速。

GCC使用三个而不是两个XMM寄存器。

Clang使用两个常量并且只为它们添加,而不是仅使用一个并使用subss从寄存器中减去一个。

我绘制了这些序列的指令级真正依赖关系,而Clang的缩短了一级。

性能方面,与我的预期完全不同, GCC版本的速度提高了约30%

我在Intel(Broadwell)和AMD(Bulldozer)CPU上测试了这个,我不明白为什么GCC代码会更快。

原始基准使用了错误的内联asm代码。在为感兴趣的函数的C ++代码创建两个目标文件(一个使用GCC,一个使用Clang)并使用GCC链接它们之后,性能差异消失了。 Peter Cordes暗示这可能是因为{{ 1}}省略-O1指令。

我查看了新基准测试方法的编译器输出,并断言.p2align的两个实现都具有与上面完全相同的代码,并且编译器实际上正在调用它们。

0 个答案:

没有答案