现代编译器的优化越来越好,使用基本优化,例如使用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;
}
使用-O3
对GCC进行编译时,可以观察到生成的浮点数由编译器计算并存储在源代码中。显然不可能进一步优化。
现在,让我们看看第二个例子:
#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到百万个位置的程序。这样的程序不需要用户输入,因此理论上结果可以由编译器硬编码到机器代码中。当然这不是一个好主意,因为编译器需要花费更长的时间来内部评估这样的程序,而不是仅运行较不优化的版本。
但是,在这种情况下,是什么让编译器决定不优化循环?是否存在优化此类代码的语言/编译器或是否存在阻止此问题的内容?它可能与无法预测程序是否会结束的概念有关吗?
答案 0 :(得分:3)
这实际上只是一个问题,即启用了哪些优化,以及编译器中实际可用的优化。一些优化,如函数内联和常量传播,基本上是普遍可用的,并且相对容易实现。因此,大多数编译器将使用大多数优化设置优化第一个程序。
第二个程序需要循环分析和循环消除来优化,这样做更加棘手。编译器可能可以优化第二个程序,但是你的编译器很可能没有优化这种循环的机制(证明浮点优化的正确性通常比证明整数的正确性要复杂得多)优化)。请注意,如果start
被声明为int
,我的GCC版本会对循环进行优化。