[解決済み] なぜコンパイラは予測可能な加算ループを乗算に最適化できないのでしょうか?
質問
の見事な回答を読んでいて思いついた質問です。 謎めいた を質問してみました。 なぜソートされた配列の方がソートされていない配列よりも処理が速いのでしょうか? ?
関係する型のコンテキストです。
const unsigned arraySize = 32768;
int data[arraySize];
long long sum = 0;
彼の回答では、Intel コンパイラ (ICC) がこれを最適化すると説明されています。
for (int i = 0; i < 100000; ++i)
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
sum += data[c];
...これと同等のものになります。
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
for (int i = 0; i < 100000; ++i)
sum += data[c];
オプティマイザはこれらが等価であると認識しているため ループを交換します。 を行い、ブランチを内部ループの外側に移動しています。とても賢いですね。
でも、なぜこうならないのでしょうか?
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
sum += 100000 * data[c];
Mysticial(または他の誰か)が同じように素晴らしい回答をしてくれることを願っています。私はその他の質問で議論された最適化について今まで知らなかったので、本当に感謝しています。
どのように解決するのですか?
コンパイラが一般に変換できない
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
for (int i = 0; i < 100000; ++i)
sum += data[c];
に
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
sum += 100000 * data[c];
というのは、後者は符号付き整数のオーバーフローを引き起こす可能性があり、前者はそうならないからです。符号付き 2 の補数整数のオーバーフローに対するラップアラウンド動作が保証されている場合でも、結果が変わってしまいます (if
data[c]
が 30000 の場合、積は
-1294967296
は、典型的な 32 ビット
int
に 30000 を加えた 100000 回が、ラップアラウンドを持つ
sum
に 30000 を加算すると、オーバーフローしない場合は
sum
を3000000000個増やすことになります)。符号なし量についても同じことが言えることに注意してください、異なる数値で、オーバーフローの
100000 * data[c]
のオーバーフローは通常
2^32
を導入することになりますが、これは最終的な結果に現れてはいけません。
に変換される可能性があります。
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
sum += 100000LL * data[c]; // resp. 100000ull
が、もし、いつものように
long long
よりも十分に大きい場合は
int
.
なぜそうしないのか、私には分かりませんが、おそらくそれは Mysticial の発言 どうやら、loop-interchange の後に loop-collapsing パスを実行しないようです。
ループインターチェンジ自体は一般的に有効ではないことに注意してください (符号付き整数の場合)、なぜなら
for (int c = 0; c < arraySize; ++c)
if (condition(data[c]))
for (int i = 0; i < 100000; ++i)
sum += data[c];
はオーバーフローを引き起こす可能性があります。
for (int i = 0; i < 100000; ++i)
for (int c = 0; c < arraySize; ++c)
if (condition(data[c]))
sum += data[c];
ではありません。この条件では、すべての
data[c]
が同じ符号を持つことを保証しているため、1つがオーバーフローすると、両方がオーバーフローします。
コンパイラがそれを考慮したかどうかはあまり自信はありませんが(@Mysticialさん、以下のような条件で試してみてください。
data[c] & 0x80
のような、正負の値に対して真となるような条件で試してみてはいかがでしょうか?) 私はコンパイラが無効な最適化を行うことがありました (たとえば、数年前、私は ICC (11.0) で
1.0/n
ここで
n
は
unsigned int
. gccの出力の約2倍の速さでした。しかし、間違って、多くの値が
2^31
よりも大きな値を出力していました。)
関連
-
_CRT_SECURE_NO_WARNINGS エラーメッセージ、解決方法
-
C 構造体定義エラー: '['トークンの前に一次式があることが予想される
-
error: 'for' loop initial declaration is only allowed in C99 mode 原因と解決方法
-
未定義の `__isoc99_sscanf' への参照
-
[解決済み] Xcode - 警告。C99 では関数の暗黙の宣言は無効です。
-
[解決済み] C言語で関数型プログラミングを行うためのツールにはどのようなものがありますか?
-
[解決済み] なぜGCCはa*a*a*a*aを(a*a*a)*(a*a*a)に最適化しないのでしょうか?
-
[解決済み] アセンブリがCより速いのはどんなとき?[クローズド]
-
[解決済み] なぜRustコンパイラは、2つのミュータブル参照がエイリアスできないと仮定してコードを最適化しないのですか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
#137: 式は変更可能なlvalueでなければならない問題 // 文字列配列の代入問題
-
C: 1を求める! + 2! + 3! + ... + n! (ループ)
-
警告: 'struct XXX' はパラメータリストの内部で宣言されています。
-
[解決済み] C言語でchar配列をコピーする方法は?
-
[解決済み] C言語で%sを正しく使う - 超基本レベル
-
[解決済み] C - Setデータ構造を実装するには?
-
[解決済み] while ( !feof (file) ) 」は、なぜいつも間違っているのですか?
-
[解決済み] C言語標準に準拠した構造体の初期化方法
-
[解決済み] なぜ16進数には0xがつくのですか?
-
[解決済み] なぜalloca()の使用はグッドプラクティスとみなされないのでしょうか?