现代编译器的优化越来越好,基本优化如常量折叠到使用 SIMD 指令。但是,我想知道这些优化应该走多远,以及现在的编译器是如何做出这种决定的。
让我们看一个例子:
#include <stdio.h>
static double naive_sin(double n) {
return n - n*n*n / 6.0 + n*n*n*n*n / 120.0 + n*n*n*n*n*n*n / 5040.0;
}
int main() {
printf("%f\n", naive_sin(1.0));
return 0;
}
当使用 GCC 编译-O3
它时,可以观察到生成的浮点数由编译器计算并存储在源代码中。进一步优化显然是不可能的。
现在,让我们看第二个例子:
#include <stdio.h>
int main() {
double start = 0.0;
for (int i = 0; i < 100; i++) {
start += 1.0;
}
printf("%f\n", start);
return 0;
}
考虑到第一个示例的结果,可以期望编译器应用类似的优化并100.0
在生成的机器代码中生成常量。但是,当查看输出时,发现循环仍然存在!
显然,这种优化并不总是可行的。假设您正在编写一个将 pi 计算到一百万个位置的程序。这样的程序不需要用户输入,因此理论上结果可以由编译器硬编码为机器代码。当然这不是一个好主意,因为编译器将花费更长的时间来内部评估这样的程序,而不是只运行优化程度较低的版本。
尽管如此,是什么让编译器决定在这种情况下不优化循环?是否有优化这种代码的语言/编译器,或者有什么东西阻止了这种情况?它是否可能与无法预测程序是否会结束的概念有关?