1. ホーム
  2. c++

[解決済み】C++のコンパイルにはなぜそんなに時間がかかるのですか?

2022-04-14 22:29:54

質問

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++をはるかに強力な言語にしてくれますが、コンパイル速度の面でも犠牲を払っています。