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
究其原因,比较运营商的这种行为<
或>=
不同是否您的double
IS NaN
或不是NaN
.NaN
返回其中一个操作数的所有比较false
.所以,你x*x < 0.0
将永远是假的不管x
是NaN
或不是.因此编译器可以安全地优化它.但是,x * x >= 0
对于NaN
非NaN
值和非值的情况将表现不同,因此编译器在程序集中保留条件跳转.
这就是cppreference关于与所涉及的NaN进行比较的说法:
转换后的操作数值以通常的数学意义进行比较(除了正负零比较相等,任何涉及NaN值的比较返回零)