c++ - ビルド - コンパイル時間とは




なぜC++のコンパイルには時間がかかりますか? (10)

C ++ファイルをコンパイルすると、C#とJavaに比べて非常に時間がかかります。 通常のサイズのPythonスクリプトを実行するよりも、C ++ファイルをコンパイルするのにかなり時間がかかります。 私は現在VC ++を使用していますが、どのコンパイラでも同じです。 どうしてこれなの?

私が考えることができる2つの理由は、ヘッダーファイルを読み込んでプリプロセッサを実行することでしたが、なぜそれが長くかかるのかを説明するようには見えません。


C ++でのプログラムのコンパイル速度に影響すると思われる2つの問題があります。

可能性のある問題#1 - ヘッダーのコンパイル:(これは、別の答えやコメントによって既に解決されている場合もあります)Microsoft Visual C ++(AKA VC ++)はプリコンパイル済みヘッダーをサポートしています。 新しいプロジェクトを作成し、作成しているプログラムの種類を選択すると、画面にセットアップウィザードのウィンドウが表示されます。 一番下にある「次へ>」ボタンを押すと、いくつかの機能のリストがあるページに移動します。 [プリコンパイル済みヘッダー]オプションの横にあるボックスがオンになっていることを確認します。 (注:これはC ++でのWin32コンソールアプリケーションでの私の経験ですが、これはC ++のすべての種類のプログラムでは当てはまりません)。

可能性のある課題#2 - 場所をコンパイルする:今年の夏、私はプログラミングコースを受講しました。使用していたラボのコンピュータが毎晩深夜に拭き取られたので、私たちはすべてのプロジェクトを8GBフラッシュドライブに保存しなければなりませんでした。それは私たちの仕事のすべてを消してしまったでしょう。 移植性/セキュリティ/ etc。のために外部記憶装置にコンパイルしている場合は、プログラムがコンパイルされるまでに非常に長い時間がかかることがあります(上記のプリコンパイルされたヘッダであっても)プログラム。 この場合のあなたのアドバイスは、使用しているコンピュータのハードドライブ上にプログラムを作成してコンパイルし、何らかの理由でプロジェクトの作業をやりたい/ 「ハードウェアを安全に取り外してメディアを取り出す」アイコンをクリックします。アイコンは小さな緑色の円の後ろに白いチェックマークがついた小さなフラッシュドライブとして表示され、接続を切断します。

これがあなたに役立つことを願っています それが私に教えてください! :)


C ++はマシンコードにコンパイルされます。 したがって、プリプロセッサ、コンパイラ、オプティマイザ、そして最後にアセンブラがあります。これらはすべて実行する必要があります。

JavaおよびC#はバイトコード/ ILにコンパイルされ、Java仮想マシン/ .NET Frameworkは実行前に実行されます(またはJITコンパイルがマシンコードに変換されます)。

Pythonは、バイトコードにコンパイルされたインタプリタ言語です。

私はこれにも他の理由があると確信していますが、一般に、ネイティブの機械語にコンパイルする必要はなく、時間が節約されます。


あなたが得ているトレードオフは、プログラムが少し速く走るということです。 それは開発中にあなたに冷たい快適さかもしれませんが、開発が完了し、プログラムがユーザーによって実行されているだけで大​​したことです。


いくつかの理由

ヘッダーファイル

コンパイル単位ごとに、(1)ロードされ、(2)コンパイルされるために、数百または数千のヘッダが必要です。 プリプロセッサは、コンパイル単位ごとにヘッダをコンパイルした結果異なる可能性があるため、コンパイル単位ごとに再コンパイルする必要あります。 (1つのコンパイル単位でマクロを定義して、ヘッダーの内容を変更することができます)。

コンパイル単位ごとに膨大な量のコードをコンパイルする必要があり、また、すべてのヘッダーを複数回コンパイルする必要があります(コンパイル単位ごとに1回)。

リンクする

コンパイルすると、すべてのオブジェクトファイルをリンクする必要があります。 これは、基本的に並列処理が難しいモノリシックなプロセスであり、プロジェクト全体を処理する必要があります。

解析

構文は、解析するのが非常に複雑であり、文脈に大きく依存しており、あいまいさを解消することは非常に困難です。 これには多くの時間がかかります。

テンプレート

C#では、 List<T>は、あなたのプログラムでListのインスタンシエーションがいくつあっても、コンパイルされる唯一の型です。 C ++では、 vector<int>vector<int>とは完全に別の型であり、それぞれを個別にコンパイルする必要があります。

これに加えて、テンプレートはコンパイラが解釈しなければならない完全なチューリング完全「サブ言語」を構成し、これはばかばかしく複雑になる可能性があります。 比較的単純なテンプレートメタプログラミングコードでも、数十件のテンプレートインスタンス化を生成する再帰的テンプレートを定義できます。 テンプレートは非常に複雑な型になるかもしれません。名前が不規則に長くなり、リンカに多くの作業が追加されます。 (シンボル名の多くを比較しなければならず、これらの名前が何千文字にもなるとかなり高価になる可能性があります)。

もちろん、ヘッダーファイルの問題は悪化します。なぜなら、テンプレートは一般にヘッダーに定義する必要があります。つまり、コンパイル単位ごとに解析してコンパイルする必要があります。 普通のCコードでは、ヘッダは通常、前方宣言のみを含みますが、実際のコードはほとんどありません。 C ++では、ほとんどすべてのコードがヘッダーファイルに存在することは珍しくありません。

最適化

C ++では非常に劇的な最適化が可能です。 C#またはJavaでは、クラスを完全に排除することはできません(リフレクションのためにそこに存在する必要があります)。しかし、単純なC ++テンプレートメタプログラムでも、数十または数百のクラスが簡単に生成できます。段階。

さらに、C ++プログラムはコンパイラによって完全に最適化されなければなりません。 AC#プログラムは、JITコンパイラに依存してロード時に追加の最適化を実行できますが、C ++ではこのような「第2のチャンス」はありません。 コンパイラが生成するものは、得られるほど最適化されています。

機械

C ++はマシンコードにコンパイルされています。これはJavaや.NETのバイトコードよりも複雑です(特にx86の場合)。 (これは、コメントなどで言及されているためにのみ記載されていますが、実際には、このステップでは合計コンパイル時間のほんのわずかしかかかりません)。

結論

これらの要素のほとんどは、実際にはかなり効率的にコンパイルされるCコードによって共有されます。 解析ステップは、C ++ではずっと複雑であり、かなり時間がかかるかもしれませんが、主な犯罪者はたぶんテンプレートです。 彼らは便利で、C ++をはるかに強力な言語にしますが、コンパイル速度の面でも有益です。


ほとんどの答えは、C#がコンパイル時に1回だけ実行されるアクションを実行するコストのために、C#が常に遅く実行されることに言及すると少し不明ですが、このパフォーマンスコストもランタイム依存性の影響を受けます実行するには)、C#プログラムのメモリフットプリントは常に高くなり、その結果、使用可能なハードウェアの機能とより密接に関連したパフォーマンスが得られます。 同じことが、VMによって解釈される他の言語にも当てはまります。


コンパイルされた言語は、常にインタープリター言語よりも大きな初期オーバーヘッドを必要とします。 また、C ++コードを非常にうまく構築していない可能性もあります。 例えば:

#include "BigClass.h"

class SmallClass
{
   BigClass m_bigClass;
}

次のものよりもかなり遅くコンパイルします。

class BigClass;

class SmallClass
{
   BigClass* m_bigClass;
}

大規模なC ++プロジェクトでコンパイル時間を短縮する簡単な方法は、プロジェクト内のすべてのcppファイルを含む* .cppインクルードファイルを作成してコンパイルすることです。 これは、ヘッダーの爆発の問題を一度減らします。 これの利点は、コンパイルエラーが引き続き正しいファイルを参照することです。

たとえば、a.cpp、b.cpp、c.cppがあるとします。ファイルを作成します:everything.cpp:

#include "a.cpp"
#include "b.cpp"
#include "c.cpp"

次に、everything.cppを作成してプロジェクトをコンパイルします。


既にコメントされているように、コンパイラは多くの時間をテンプレートのインスタンス化とインスタンス化に費やしています。 そのような延長には、その特定のアイテムに焦点を当てたプロジェクトがあり、本当に好都合な場合には30倍のスピードアップが可能です。 http://www.zapcc.comを参照してください


減速は、どのコンパイラでも必ずしも同じではありません。

私はDelphiやKylixを使用していませんでしたが、MS-DOS時代には、Turbo Pascalプログラムはほぼ即座にコンパイルされ、同等のTurbo C ++プログラムはちょうどクロールされます。

2つの大きな違いは、非常に強力なモジュールシステムとシングルパスコンパイルを可能にする構文でした。

確かにコンパイル速度はC ++コンパイラ開発者の優先事項ではない可能性がありますが、C / C ++構文には固有の複雑さがあり、処理が難しくなります。 (私はCの専門家ではありませんが、Walter Brightは様々な商用C / C ++コンパイラを構築した後、彼はD言語を作ったのですが、彼の変更の1つは 、文脈自由文法を強制して、 。)

また、一般にMakefileはCで別々にコンパイルされるように設定されているので、10個のソースファイルがすべて同じインクルードファイルを使用する場合、そのインクルードファイルは10回処理されます。


解析とコード生成は実際にはかなり高速です。 実際の問題は、ファイルの開閉です。 インクルードガードを使用していても、コンパイラは引き続き.Hファイルを開き、各行を読み込んだ後で無視します。

一度(職場で退屈な)友人は、彼の会社のアプリケーションを取り出し、すべてのソースファイルとヘッダーファイルを1つの大きなファイルにまとめました。 コンパイル時間が3時間から7分に短縮されました。







compilation