2

现代编译器的优化越来越好,基本优化如常量折叠到使用 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 计算到一百万个位置的程序。这样的程序不需要用户输入,因此理论上结果可以由编译器硬编码为机器代码。当然这不是一个好主意,因为编译器将花费更长的时间来内部评估这样的程序,而不是只运行优化程度较低的版本。

尽管如此,是什么让编译器决定在这种情况下不优化循环?是否有优化这种代码的语言/编译器,或者有什么东西阻止了这种情况?它是否可能与无法预测程序是否会结束的概念有关?

4

1 回答 1

3

这实际上只是启用了哪些优化以及编译器中实际可用的优化的问题。一些优化,比如函数内联和常量传播,基本上是普遍可用的并且相对容易实现。因此,大多数编译器会优化具有最多优化设置的第一个程序。

第二个程序需要循环分析和循环消除来优化,这要复杂得多。编译器可能优化第二个程序,但您的编译器很可能没有优化此类循环的机制(证明浮点优化的正确性通常比证明整数优化的正确性要复杂得多)。请注意,如果start声明为int.

于 2013-08-02T23:21:52.470 回答