反復回数がわかっているが大きいループを展開するように 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を使用しています。
何か案は?不足しているフラグまたはパラメーターはありますか...?