[解決済み] 単純なループが最適化されるのは、なぜですか?
質問
この単純なループを考えてみましょう。
float f(float x[]) {
float p = 1.0;
for (int i = 0; i < 959; i++)
p += 1;
return p;
}
gcc 7 (snapshot)またはclang (trunk)でコンパイルする際に
-march=core-avx2 -Ofast
に非常に似たものを得ることができます。
.LCPI0_0:
.long 1148190720 # float 960
f: # @f
vmovss xmm0, dword ptr [rip + .LCPI0_0] # xmm0 = mem[0],zero,zero,zero
ret
言い換えれば、ループせずに答えを960に設定するだけです。
しかし、もしあなたがこのコードを
float f(float x[]) {
float p = 1.0;
for (int i = 0; i < 960; i++)
p += 1;
return p;
}
生成されたアセンブリは実際にループサムを実行するのですか?例えばclangが出します。
.LCPI0_0:
.long 1065353216 # float 1
.LCPI0_1:
.long 1086324736 # float 6
f: # @f
vmovss xmm0, dword ptr [rip + .LCPI0_0] # xmm0 = mem[0],zero,zero,zero
vxorps ymm1, ymm1, ymm1
mov eax, 960
vbroadcastss ymm2, dword ptr [rip + .LCPI0_1]
vxorps ymm3, ymm3, ymm3
vxorps ymm4, ymm4, ymm4
.LBB0_1: # =>This Inner Loop Header: Depth=1
vaddps ymm0, ymm0, ymm2
vaddps ymm1, ymm1, ymm2
vaddps ymm3, ymm3, ymm2
vaddps ymm4, ymm4, ymm2
add eax, -192
jne .LBB0_1
vaddps ymm0, ymm1, ymm0
vaddps ymm0, ymm3, ymm0
vaddps ymm0, ymm4, ymm0
vextractf128 xmm1, ymm0, 1
vaddps ymm0, ymm0, ymm1
vpermilpd xmm1, xmm0, 1 # xmm1 = xmm0[1,0]
vaddps ymm0, ymm0, ymm1
vhaddps ymm0, ymm0, ymm0
vzeroupper
ret
これはなぜでしょうか、そしてなぜclangとgccで全く同じなのでしょうか?
を置き換えた場合の同じループの限界は
float
を
double
は479です。これはまたgccとclangでも同じです。
アップデート1
gcc 7 (snapshot) と clang (trunk) では挙動が大きく異なることがわかりました。clang は私が知る限り、960以下のすべての制限に対してループを最適化します。例えば ではありません。 ループを最適化しませんが、(他の多くの値と同様に) は はループを最適化します。
どのように解決するのですか?
TL;DR
デフォルトでは、現在のスナップショットGCC 7は矛盾した振る舞いをしますが、以前のバージョンでは、以下の理由によりデフォルトで制限されています。
PARAM_MAX_COMPLETELY_PEEL_TIMES
によるデフォルトの制限は16です。コマンドラインからオーバーライドすることができます。
この制限の根拠は、あまりに積極的なループの展開を防ぐためで、これは 諸刃の剣 .
GCCバージョン <= 6.3.0
GCCに関連する最適化オプションは
-fpeel-loops
で、これは、フラグ
-Ofast
というフラグとともに間接的に有効になります (強調は私です)。
あまりロールしないという十分な情報があるループを剥がします。 ロールしないことを示す十分な情報があるループのピール(プロファイルのフィードバックや 静的解析 ). また 完全なループ・ピーリング(すなわち ループの完全な除去を行い、小さな ループの完全除去 ).
で有効になります。
-O3
または-fprofile-use
.
より詳細な情報を得るには
-fdump-tree-cunroll
:
$ head test.c.151t.cunroll
;; Function f (f, funcdef_no=0, decl_uid=1919, cgraph_uid=0, symbol_order=0)
Not peeling: upper bound is known so can unroll completely
メッセージの送信元は
/gcc/tree-ssa-loop-ivcanon.c
:
if (maxiter >= 0 && maxiter <= npeel)
{
if (dump_file)
fprintf (dump_file, "Not peeling: upper bound is known so can "
"unroll completely\n");
return false;
}
そのため
try_peel_loop
という関数が返されます。
false
.
より詳細な出力は
-fdump-tree-cunroll-details
:
Loop 1 iterates 959 times.
Loop 1 iterates at most 959 times.
Not unrolling loop 1 (--param max-completely-peeled-times limit reached).
Not peeling: upper bound is known so can unroll completely
で囲むことで、制限を微調整することができます。
max-completely-peeled-insns=n
と
max-completely-peel-times=n
のパラメータを指定します。
max-completely-peeled-insns
完全に剥がれたループの最大インスンス数。
max-completely-peel-times
完全なピーリングに適したループの最大反復回数を指定します。 を指定します。
insnsについてより詳しく知るには、以下のサイトを参照してください。 GCC内部マニュアル .
例えば、以下のようなオプションでコンパイルした場合。
-march=core-avx2 -Ofast --param max-completely-peeled-insns=1000 --param max-completely-peel-times=1000
とすると、コードが変身します。
f:
vmovss xmm0, DWORD PTR .LC0[rip]
ret
.LC0:
.long 1148207104
クラング
Clang が実際に何をするのか、どのようにその限界を調整するのかは分かりませんが、私が観察したところ、ループに 展開プラグマ とマークすれば、完全に削除されます。
#pragma unroll
for (int i = 0; i < 960; i++)
p++;
の結果になります。
.LCPI0_0:
.long 1148207104 # float 961
f: # @f
vmovss xmm0, dword ptr [rip + .LCPI0_0] # xmm0 = mem[0],zero,zero,zero
ret
関連
-
警告:代入がキャストなしで整数からポインタを作成する場合の修正方法に関する警告
-
[解決済み] Valgrind が初期化されていないバイトについて警告する
-
[解決済み] c または c++ 用のシンプルな 2 次元クロスプラットフォームグラフィックスライブラリ?[クローズド]
-
[解決済み] C言語で関数をパラメータとして渡すにはどうすればよいですか?
-
[解決済み] 演算子 *, /, +, -, % を使わずに 3 で割る。
-
[解決済み] printfは、フォーマット文字列の中に改行がないと、呼び出し後にフラッシュしないのはなぜですか?
-
[解決済み] なぜalloca()の使用はグッドプラクティスとみなされないのでしょうか?
-
[解決済み] 2次元配列の反復処理において、ループの順序がパフォーマンスに影響するのはなぜですか?
-
[解決済み】文字列リテラルで初期化された "char *s "に書き込むと、"char s[]"ではなく、セグメンテーションフォールトが発生するのはなぜですか?]
-
[解決済み] なぜラムダはプレーンな関数よりもコンパイラによって最適化できるのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
C - 添え字の値が配列でもポインタでもベクトルでもないエラー
-
[解決済み] C言語の書式指定子 %ul と %lu の違いは何ですか?
-
[解決済み] C関数から文字列を返す
-
[解決済み] C言語で関数型プログラミングを行うためのツールにはどのようなものがありますか?
-
[解決済み] "static const" vs "#define" vs "enum"
-
[解決済み] mallocの結果はキャストするのですか?
-
[解決済み] C言語でのブーリアン値の使用
-
[解決済み] printfにおけるdoubleの正しい書式指定子
-
[解決済み] C言語でファイルが存在するかどうかを確認する最も良い方法は何ですか?
-
[解決済み] アセンブリがCより速いのはどんなとき?[クローズド]