为什么编译器不优化这个初始化?

Why does the compiler not optimize this initialization?

考虑以下 C 代码:

extern void foo(int* ip);

void myfunc(void)
{
    int arr[15] = {0};
    for (int i=0; i<10; i++)
    {
        arr[i] = 42;
    }

    foo(arr);
}

我尝试使用 gcc 和 clang,使用 -O3-Os。在所有情况下,编译后的程序集都会写入所有 15 个零,然后用 42 覆盖其中的 10 个。

我想可能只是还没有针对这种情况编写优化,但对我来说这似乎是一个相当明显和常见的情况。是否存在阻止优化的因素?

我在 x86-32 Linux 上使用了这些命令:

gcc -std=c99 -S -O3 hello.c
clang -std=c99 -S -O3 hello.c

这不是一个非常科学的解释,只是一种直觉(不过,我碰巧知道 一些 GCC 的内部结构)。

为了可靠地进行您想要的优化,编译器必须管理 子数组 切片 。然后它变得非常复杂且容易出错。优化这么多的编译器可能会消耗大量内存(用于子数组的符号表示)和大量编译时间。这通常不值得付出努力(最好在编译器内部花费以优化循环)。

顺便说一句,GCC 有一个插件框架和 MELT 扩展(MELT 是一种用于扩展 GCC 的 lispy 领域特定语言,我是 MELT 的主要作者)。因此,您可以尝试添加一个新的优化通道(通过 MELT 扩展或某些 C++ 插件)来完成这项工作。您很快就会意识到,您的传递要么非常具体,要么需要处理大量 GCC 内部表示,并且可能会耗尽编译时间和内存而获得的收益微乎其微。

请注意,GCC 和 Clang 都巧妙地展开了两个循环(这在性能方面很重要)。

顺便说一句,Frama-C(由同事开发的用于 C 程序的静态分析器)值分析器似乎能够推断出关于您的 arr

的良好属性

所以,请随意将优化添加到 GCC。如果您不知道(或没有时间 - 数月或数年)如何添加它,请随时向能够根据您的需要增强 GCC 的公司或组织付费。这可能是一百万欧元(或美元)/ 3 年的项目才能使该优化在有趣的案例上发挥作用。

如果你真的要花这么多钱,请通过电子邮件与我联系。

具有这种优化的编译器需要一些启发式方法来禁用它们(例如,如果 arr 是一个包含百万成员的数组,而您正在编写一些 sieve of Erasthothenes,这可能不值得编译器努力在编译时保留复合索引子片的所有联合。

顺便说一句,你会接受一个慢 20 倍的优化编译器(在编译时更慢)以获得收益(在 运行 时间可能是百分之几)这在实践中很少发生并且不是很重要?最后,我不认为这是优化的常见情况。 YMMV.

您可能对 PIPS4U

等源到源转换器感兴趣