【发布时间】:2021-12-05 08:25:43
【问题描述】:
我将它插入 Godbolt 并惊喜地发现这两个函数调用 a() 和 b() 在 -O0 以外的任何其他条件下都是等效的(使用大多数主要编译器):
#include <cmath>
struct A {
int a,b,c;
float bar() {
return sqrt(a + b + c);
}
};
struct B {
int a[3];
float bar() {
int ret{0};
for (int i = 0; i<3; ++i) {
ret += a[i];
}
return sqrt(ret);
}
};
float a() {
A a{55,67,12};
return a.bar();
}
float b() {
B b{55,67,12};
return b.bar();
}
Godbolt 输出为:
a():
movss xmm0, DWORD PTR .LC0[rip]
ret
b():
movss xmm0, DWORD PTR .LC0[rip]
ret
.LC0:
.long 1094268577
我不是装配专家,但我想知道这是否真的是真的,他们正在做同样的工作。我什至看不到这个程序集中的哪个位置调用了sqrt,或者long“常量”(?)在那里做什么。
【问题讨论】:
-
我不明白这个问题。您无需成为装配专家即可看到
a()和b()的装配是相同的。您实际上是在问为什么编译器可以优化这两个函数来做同样的事情吗? -
编译器在编译期间计算结果并在程序集中使用计算出的常数。
1094268577应按位解释为浮点数 (IEEE-754):1094268577=0x413936A1=11.5758371353。试试自己here。 -
简短回答:这两个函数具有完全相同的可观察行为。
-
当内联+常量传播可以使输入成为编译时常量时,它不会在运行时执行sqrt;它在编译时评估 sqrt。
-
a+b+c = a+b+c 是的。您没有使用任何全局变量或 volatile 或其他任何东西来防止消除死代码,所以它消除了它。
标签: c++ assembly x86-64 compiler-optimization