如何说服 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;
}
...if CONSTANT_COUNT
定义为 8(或更少),那么 GCC 将展开循环,传播常量,并将整个函数简化为一个简单的函数return <value>;
。另一方面,如果CONSTANT_COUNT
是 9 (或更大),则循环不会展开,并且 GCC 会生成一个二进制文件,该二进制文件会循环、读取常量并在运行时添加它们 - 尽管从理论上讲,该函数仍然可以优化为仅返回持续的。 (是的,我看过反编译的二进制文件。)
If I manually展开循环,如下所示:
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;
}
Or this:
#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
是可行的选择,因为有some情况下CONSTANT_COUNT
是一个运行时表达式,并且same对于这些情况,代码仍然需要正确工作。 (在这些情况下,性能并不那么重要。)
我正在使用 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
, and max-completely-peel-times
,这些似乎都没有什么区别。
我在 Linux x86_64 上使用 GCC 4.8.2。
有任何想法吗?是否有我缺少的标志或参数...?