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

标签 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;
}

...如果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;
}

或者这个:

#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;
}

...然后函数被优化为返回一个常量。因此,一旦展开,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。

有什么想法吗?是否有我缺少的标志或参数...?

最佳答案

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

int
get_sum()
{
  int total = 0;
  int i, j, k = 0;
  for (j = 0; j < 2; ++j)
    {
      for (i = 0; i < CONSTANT_COUNT / 2; ++i)
        {
          total += constants[k++];
        }
    }
  if (CONSTANT_COUNT % 2)
    total += constants[k];
  return total;
}

我只给它传递了 -O3 标志。 get_sum 的汇编代码真的只是

movl $561, %eax
ret

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

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

关于c - 强制/说服/欺骗 GCC 展开 _Longer_ 循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25878821/

相关文章:

c++ - 防止 GCC 在基准代码中移动代码

Java可以识别CPU的SIMD优势;或者只是循环展开的优化效果

c++ - 编译器 (gcc) 可以保证 c++ 循环吗?

c - void 类型到底是什么,它是如何工作的?

java - 处理循环特殊情况

c - 访问 .rodata 部分中的特定数据时应用程序崩溃

c++ - 是否可以使用宏来处理定义的内容?

c - IF - Else 语句不起作用

c++ - Cygwin C++ 编译器