よくあるコンパイラの誤解
(sbaziotis.com)コンパイラ最適化に関する誤解
- 最適化は最適なプログラムを提供する?
- コンパイラの目標は最適なプログラムを生成することではなく、単純化されたプログラムを改善することです。
- コードサイズの最適化は可能性がありますが、実行時間の最適化は、計測の難しさ、最適部分構造の欠如、ハードウェアモデルの不正確さなどにより困難です。
- 実行時間はコードサイズと異なり正確な測定が難しく、複数の要因の影響を受け、最適部分構造を持ちません。たとえば、2つのループを個別に最適化しても、プログラム全体を最適化するには2つのループを結合する必要があるかもしれません。また、コンパイル対象のハードウェアに対する正確なモデルがないため、最適化は困難です。たとえば、goSLP はグローバルに最適化された SLP ベクトル化コードを生成しますが、ハードウェアモデルが不正確であるため、生成されたプログラムは最適ではないだけでなく、LLVM より遅い場合もあります。
分岐予測に関する誤解
- 分岐重みは CPU の分岐予測器で使われる?
- x86 アーキテクチャでは、コンパイラは分岐ヒントを生成しません。
- 分岐重みはコンパイラのコードブロック配置に使われます。(例: 分岐の可能性が高い場合、命令キャッシュの局所性を高めるために対象ブロックを現在のブロックの直下に配置)
- 最近の Intel Redwood Cove アーキテクチャでは分岐ヒントが再び意味を持つようになりましたが、実際にコンパイラがこうしたヒントを生成するケースはまれです。
最適化レベルに関する誤解
- -O3 は -O2 よりはるかに高速なコードを生成する?
- Clang の場合、-O2 と -O3 の性能差は大きくなく、GCC の場合は -O2 が Clang より積極的ではないため、多少の差があります。
- -O3 はコードサイズをほとんど考慮しないため、命令キャッシュの問題が発生する可能性があります。
- ベンチマークで確認するのがよいです。
Javascript インタプリタと JIT コンパイラに関する誤解
- Javascript インタプリタが実行時に JIT コンパイルを行うのは、どの経路がホットか事前に分からないから?
- ホットパスを知っているだけでは不十分で、型情報も必要です。
- 型情報は実行時にしか分からないため、JIT コンパイラは実行時にコードをコンパイルします。
コンパイラとインタプリタの関係に関する誤解
- コンパイラがあればインタプリタは不要?
- C/C++ ではインタプリタは有用ではありませんが、WebAssembly のようなケースでは、開発や利用のしやすさ、デバッグ、セキュリティなどの利点を提供できます。
コンパイラ中間段階に関する誤解
- 中間段階(middle-end)はターゲット/プラットフォームに依存しない?
- LLVM の場合、中間段階はターゲット/プラットフォームから完全に独立しているわけではありません。
データ局所性の最適化に関する誤解
- コンパイラはデータ局所性を最適化する?
- コンパイラは命令キャッシュの局所性は最適化しますが、データ局所性はほとんど最適化しません。
- データ局所性の最適化にはコードへの大規模な変更が必要であり、C/C++ コンパイラはそのような変更を行えません。
- データ局所性を改善するには、データ指向設計のような手法を使う必要があります。
コンパイル速度に関する誤解
- -O0 は高速なコンパイルを提供する?
- -O0 はデバッグしやすく予測可能なコードを生成しますが、常に高速なコンパイルを保証するわけではありません。
- 一般には -O0 のほうが -O2 より速いですが、プロジェクトの規模やコンパイラによって異なる場合があります。
- 高速なコンパイルのためには、標準のコンパイルパイプラインを迂回する方法(例: TinyCC)や、LLVM IR を直接生成する方法を検討できます。
テンプレートのコンパイル速度に関する誤解
- テンプレートはコンパイル速度が遅い?
- C++ テンプレートのコンパイルが遅いのは、C++ のコンパイルモデルのためです。
- テンプレート自体がコンパイル速度を大きく低下させるわけではありません。
- Dlang の標準ライブラリ Phobos は多くのテンプレートを使っていますが、高速にコンパイルされます。
個別コンパイルの有用性に関する誤解
- 個別コンパイルは常に価値がある?
- 個別コンパイルではリンク時間が長くなることがあります。
- 多くのプロジェクトでは、ユニティビルド(すべてのコードを単一ファイルに含める方式)のほうがより良い性能を提供します。
- ユニティビルドには、プログラム全体の最適化、コンパイル速度の向上、エラーログの改善などの利点があります。
- 個別コンパイルがユニティビルドより優れているケースはまれです。
リンク時最適化(LTO)に関する誤解
- リンク時最適化(LTO)はなぜリンク時に行われるのか?
- LTO はプログラム全体の最適化のために実行されます。
- 理論上は中間段階でプログラム全体の最適化を行うほうが合理的ですが、C/C++ ビルドシステムの現実的な問題(ソースファイルの探索や呼び出し関係の把握の難しさ)のため、リンク時に行われます。
- リンカはすべてのオブジェクトファイルを見つけられるため、コンパイラはリンカがアクセスできるよう、オブジェクトファイルに LLVM IR のような中間言語表現を含めます。
インライン化最適化に関する誤解
- インライン化は主に関数呼び出し命令を削除するから有用?
- 関数呼び出し命令を削除すること自体にも利点はありますが、インライン化の最大の利点は、ほかの最適化を可能にすることです。
- インライン化は関数間最適化を可能にします。
- インライン化によって複数の関数のコードが1つの関数に統合されると、従来の関数内最適化手法を適用できます。
inline キーワードの役割に関する誤解
- inline キーワードはインライン化最適化と関係がある?
- C++ の inline キーワードはもともと最適化器へのヒントとして使われていましたが、C++98 以降は「複数定義を許可する」という意味になりました。
- LLVM では inline キーワードがあると inlinehint 属性が追加され、インライン化のしきい値が上がりますが、その影響は大きくありません。
- 関数を常にインライン化したい場合は
always_inline指定子を使う必要があります。
コンパイラ学習資料に関する誤解
- LLVM は学ぶのに最適なコンパイラ?
- LLVM には教育的な側面もありますが、さまざまなユースケースを支えるため複雑かつ大規模です。
- コンパイラ開発を学ぶには、Go コンパイラ、LDC、DMD など、より小さく単純なコンパイラを先に見たほうがよいです。
未定義動作(Undefined Behavior)に関する誤解
-
未定義動作は最適化を可能にするだけ?
- 未定義動作は最適化を無効化してしまうこともあります。
-
コンパイラは未定義動作を「単純に」定義できる?
- コンパイラは未定義動作を定義できますが、性能に影響する可能性があります。
- コンパイラがすべての未定義動作をプラットフォームの挙動として定義するのが理想ですが、現実的には容易ではありません。
AI ベースのコード生成に関する誤解
- 99% の正確さのコード生成で十分?
- コンパイラで生成されるコードに 99% の正確さしかない場合、実用は非常に難しいです。
- コードの 1% の誤りでも、デバッグや保守に大きな困難をもたらします。
- 大規模プロジェクトでは、1% の誤りが非常に深刻な問題を引き起こすことがあります。
- 現在の LLM はコンパイラに比べて非常に遅いため、オンラインのコード生成には適していません.
まだコメントはありません。