强制/说服/欺骗GCC展开_Longer_循环?

jgu*_*son 9 c gcc loop-unrolling

我如何说服GCC展开一个已知迭代次数但又很大的循环?

我正在编译-O3.

当然,有问题的真实代码更复杂,但这是一个具有相同行为的简化示例:

int const constants[] = { 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144 };

int get_sum_1()
{
    int total = 0;
    for (int i = 0; i < CONSTANT_COUNT; ++i)
    {
        total += constants[i];
    }
    return total;
}
Run Code Online (Sandbox Code Playgroud)

...如果CONSTANT_COUNT被定义为8(或更少),那么GCC将展开循环,传播常量,并将整个函数简化为简单return <value>;.另一方面,如果CONSTANT_COUNT是9(或更高),那么循环不会展开,并且GCC会生成一个二进制循环,读取常量,并在运行时添加它们 - 即使理论上,该函数仍然可以被优化到只返回一个常数.(是的,我看过反编译的二进制文件.)

如果我手动展开循环,如下所示:

int get_sum_2()
{
    int total = 0;
    total += constants[0];
    total += constants[1];
    total += constants[2];
    total += constants[3];
    total += constants[4];
    total += constants[5];
    total += constants[6];
    total += constants[7];
    total += constants[8];
    //total += constants[9];
    return total;
}
Run Code Online (Sandbox Code Playgroud)

或这个:

#define ADD_CONSTANT(z, v, c) total += constants[v];

int get_sum_2()
{
    int total = 0;
    BOOST_PP_REPEAT(CONSTANT_COUNT, ADD_CONSTANT, _)
    return total;
}
Run Code Online (Sandbox Code Playgroud)

...然后将函数优化为返回常量.因此,一旦展开,GCC似乎能够处理较大循环的常量传播; 挂起似乎只是让GCC考虑首先展开更长的循环.

但是,既不是手动展开也不BOOST_PP_REPEAT是可行的选项,因为在某些情况下CONSTANT_COUNT运行时表达式,并且相同的代码仍然需要在这些情况下正常工作.(在这些情况下,性能不是那么重要.)

我在C(不是C++)工作,所以我也没有模板元编程constexpr.

我试过-funroll-loops,-funroll-all-loops,-fpeel-loops,和设置较大的值max-unrolled-insns,max-average-unrolled-insns,max-unroll-times,max-peeled-insns,max-peel-times,max-completely-peeled-insns,和max-completely-peel-times,其中没有一个似乎有所作为.

我在Linux,x86_64上使用GCC 4.8.2.

有任何想法吗?有没有我缺少的旗帜或参数......?

5go*_*der 3

我不确定此解决方法是否适用于您的实际问题,但我发现运行 Parabola GNU/Linux 的 x86_64 上的 GCC 4.9.0 20140604(预发布)将以下循环展开到并包括CONSTANT_COUNT == 33.

\n\n
int\nget_sum()\n{\n  int total = 0;\n  int i, j, k = 0;\n  for (j = 0; j < 2; ++j)\n    {\n      for (i = 0; i < CONSTANT_COUNT / 2; ++i)\n        {\n          total += constants[k++];\n        }\n    }\n  if (CONSTANT_COUNT % 2)\n    total += constants[k];\n  return total;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我只是把-O3标志传递给了它。的汇编代码get_sum实际上只是

\n\n
movl $561, %eax\nret\n
Run Code Online (Sandbox Code Playgroud)\n\n

我没有尝试,但也许该模式可以进一步扩展。

\n\n

对我来说,这似乎很奇怪,因为 \xe2\x80\x93 至少对我的人眼 \xe2\x80\x93 来说,代码现在看起来要复杂得多。不幸的是,这是一种相当侵入性的强制展开方式。编译器标志会好得多。

\n