生成的浮点比较汇编的差异<和> =

pla*_*cel 11 c++ floating-point assembly compiler-optimization

我正在尝试生成的程序集,发现了一件有趣的事情.有两个函数执行相同的计算.它们之间的唯一区别在于结果总和的方式.

#include <cmath>

double func1(double x, double y)
{
  double result1;
  double result2;

  if (x*x < 0.0) result1 = 0.0;
  else
  {
    result1 = x*x+x+y;
  }

  if (y*y < 0.0) result2 = 0.0;
  else
  {
    result2 = y*y+y+x;
  }

  return (result1 + result2) * 40.0;
}

double func2(double x, double y)
{
  double result = 0.0;

  if (x*x >= 0.0)
  {
    result += x*x+x+y;
  }

  if (y*y >= 0.0)
  {
    result += y*y+y+x;
  }

  return result * 40.0;
}
Run Code Online (Sandbox Code Playgroud)

由x86 clang 3.7和gcc.godbolt.org上的-O2开关生成的程序集是如此不同和意想不到的.(在gcc上编译导致类似的程序集)

    .LCPI0_0:
    .quad   4630826316843712512     # double 40
func1(double, double):                             # @func1(double, double)
    movapd  %xmm0, %xmm2
    mulsd   %xmm2, %xmm2
    addsd   %xmm0, %xmm2
    addsd   %xmm1, %xmm2
    movapd  %xmm1, %xmm3
    mulsd   %xmm3, %xmm3
    addsd   %xmm1, %xmm3
    addsd   %xmm0, %xmm3
    addsd   %xmm3, %xmm2
    mulsd   .LCPI0_0(%rip), %xmm2
    movapd  %xmm2, %xmm0
    retq

.LCPI1_0:
    .quad   4630826316843712512     # double 40
func2(double, double):                             # @func2(double, double)
    movapd  %xmm0, %xmm2
    movapd  %xmm2, %xmm4
    mulsd   %xmm4, %xmm4
    xorps   %xmm3, %xmm3
    ucomisd %xmm3, %xmm4
    xorpd   %xmm0, %xmm0
    jb  .LBB1_2
    addsd   %xmm2, %xmm4
    addsd   %xmm1, %xmm4
    xorpd   %xmm0, %xmm0
    addsd   %xmm4, %xmm0
.LBB1_2:
    movapd  %xmm1, %xmm4
    mulsd   %xmm4, %xmm4
    ucomisd %xmm3, %xmm4
    jb  .LBB1_4
    addsd   %xmm1, %xmm4
    addsd   %xmm2, %xmm4
    addsd   %xmm4, %xmm0
.LBB1_4:
    mulsd   .LCPI1_0(%rip), %xmm0
    retq
Run Code Online (Sandbox Code Playgroud)

func1编译成无分支的程序集,涉及的指令少得多func2.因此func2预计会慢得多func1.

有人可以解释这种行为吗?

Ros*_*lav 15

究其原因,比较运营商的这种行为<>=不同是否您的doubleIS NaN或不是NaN.NaN返回其中一个操作数的所有比较false.所以,你x*x < 0.0永远是假的不管xNaN或不是.因此编译器可以安全地优化它.但是,x * x >= 0对于NaNNaN值和非值的情况将表现不同,因此编译器在程序集中保留条件跳转.

这就是cppreference关于与所涉及的NaN进行比较的说法:

转换后的操作数值以通常的数学意义进行比较(除了正负零比较相等,任何涉及NaN值的比较返回零)