Как мне убедить 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
, ни одно из которых, похоже, не имеет значения.
Я использую GCC 4.8.2 в Linux, x86_64.
Любые идеи? Есть ли флаг или параметр, который мне не хватает...?