[解決済み】C++のコンパイルにはなぜそんなに時間がかかるのですか?
質問
C++のファイルをコンパイルすると、C#やJavaと比較して非常に時間がかかります。C++ファイルのコンパイルには、通常サイズのPythonスクリプトを実行するよりもかなり時間がかかります。現在VC++を使用していますが、どのコンパイラでも同じです。これはなぜでしょうか?
考えられる理由は、ヘッダーファイルの読み込みとプリプロセッサの実行の2つですが、それだけでは時間がかかる理由が説明できないように思います。
解決方法は?
いくつかの理由
ヘッダーファイル
一つのコンパイルユニットごとに、何百、何千ものヘッダーを (1) ロードし、 (2) コンパイルする必要があります。 通常、これらのヘッダはコンパイルユニットごとに再コンパイルされなければなりません。 なぜなら、プリプロセッサはヘッダをコンパイルした結果が かもしれない は各コンパイル単位で異なる。 (あるコンパイルユニットで、ヘッダの内容を変更するマクロが定義されるかもしれません)。
これはおそらく その 主な理由は、コンパイル単位ごとに膨大な量のコードをコンパイルしなければならないからです。 さらに、すべてのヘッダーを複数回コンパイルする必要があります。 (を含む各コンパイル単位で1回)。
リンク
コンパイルが完了したら、すべてのオブジェクトファイルをリンクする必要があります。 これは基本的にモノリシックな処理で、並列化することはあまりできず、プロジェクト全体を処理しなければなりません。
パース
この構文は解析が非常に複雑で、文脈に大きく依存し、曖昧さをなくすのが非常に困難です。 これには多くの時間がかかります。
テンプレート
C#の場合
List<T>
は、プログラム中にリストのインスタンスがいくつあっても、コンパイルされる唯一の型です。
C++では
vector<int>
とは完全に別の型です。
vector<float>
で、それぞれを別々にコンパイルする必要があります。
さらに、テンプレートはチューリング完全なサブ言語を構成しており、コンパイラはそれを解釈しなければなりません。 と、とんでもなく複雑になります。 比較的単純なテンプレートメタプログラミングのコードでさえ、何十ものテンプレートのインスタンスを作成する再帰的なテンプレートを定義することができます。 また、テンプレートは非常に複雑で、とんでもなく長い名前を持つ型になり、リンカに多くの余分な仕事をさせることになります。 (リンカは多くのシンボル名を比較しなければならず、これらの名前が何千文字にもなる場合、かなり高価になります)。
そしてもちろん、ヘッダーファイルの問題を悪化させます。なぜなら、テンプレートは一般的にヘッダーで定義されなければならないからです。 つまり、コンパイル単位ごとに、はるかに多くのコードを解析し、コンパイルしなければならないのです。 プレーンなCのコードでは、ヘッダは通常、前方宣言だけを含み、実際のコードはほとんどありません。 C++では、ほとんどすべてのコードがヘッダーファイル内に存在することも珍しくありません。
最適化
C++では、非常に劇的な最適化が可能です。 C#やJavaでは、クラスを完全に排除することはできません(反映のために存在させる必要があります)。 しかし、単純なC++のテンプレートメタプログラムでさえ、数十から数百のクラスを簡単に生成することができます。 そのすべてをインライン化し、最適化の段階で再び削除します。
さらに、C++のプログラムは、コンパイラによって完全に最適化されなければならない。 C#のプログラムは、JITコンパイラがロード時に追加の最適化を行うことに依存することができます。 C++には、そのような「セカンドチャンス」はない。コンパイラが生成するものは、その時点で最適化されたものなのです。
機械
C++はマシンコードにコンパイルされますが、Javaや.NETが使用するバイトコードよりも多少複雑な場合があります(特にx86の場合)。 (これはコメントなどで言及されていたため、念のため記載しています。 実際には、このステップにかかる時間は、コンパイル時間全体のごくわずかなものです。)
まとめ
これらの要因のほとんどはC言語のコードに共通しており、実際、かなり効率的にコンパイルすることができます。 C++では構文解析の手順がかなり複雑で、より多くの時間を費やすことになりますが、主な原因はおそらくテンプレートでしょう。 テンプレートは便利で、C++をはるかに強力な言語にしてくれますが、コンパイル速度の面でも犠牲を払っています。
関連
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] using namespace std;」はなぜバッドプラクティスだと言われるのですか?
-
[解決済み] B "の印刷が "#"の印刷より劇的に遅いのはなぜですか?
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] 0.1fを0にすると、なぜ10倍もパフォーマンスが落ちるのですか?
-
[解決済み] 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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】Visual Studio 2015で「非標準の構文。'&'を使用してメンバーへのポインターを作成します」エラー
-
[解決済み] [Solved] Error C1083: Cannot open include file: 'stdafx.h'
-
[解決済み】 != と =! の違いと例(C++の場合)
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み] クラスにデフォルトコンストラクタが存在しない。
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み】クラスのコンストラクタへの未定義参照、.cppファイルの修正も含む
-
[解決済み] 数値定数の前にunqualified-idを付けて、数値を定義することを期待する。
-
[解決済み】変数やフィールドがvoid宣言されている