590

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

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

4

15 に答える 15

860

いくつかの理由

ヘッダファイル

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

これがおそらく主な理由ですコンパイル単位ごとに大量のコードをコンパイルする必要があり、さらに、すべてのヘッダーを複数回 (それを含むコンパイル単位ごとに 1 回) コンパイルする必要があるためです。

リンキング

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

解析中

構文は解析が非常に複雑で、コンテキストに大きく依存し、明確にするのが非常に困難です。これには多くの時間がかかります。

テンプレート

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

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

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

最適化

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

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

機械

C++ は、バイトコード Java または .NET の使用 (特に x86 の場合) よりも多少複雑なマシン コードにコンパイルされます。(これは、コメントなどで言及されているという理由だけで、完全を期すために言及されています。実際には、このステップは、総コンパイル時間のほんの一部を超えることはほとんどありません)。

結論

これらの要因のほとんどは、実際にはかなり効率的にコンパイルされる C コードによって共有されます。解析ステップは C++ でははるかに複雑で、かなり多くの時間がかかる可能性がありますが、主な違反者はおそらくテンプレートです。これらは便利で、C++ をはるかに強力な言語にしますが、コンパイル速度の面でも犠牲になります。

于 2008-11-25T18:38:22.417 に答える
50

解析とコード生成は実際にはかなり高速です。本当の問題はファイルを開いたり閉じたりすることです。インクルードガードを使用しても、コンパイラは.Hファイルを開いて、各行を読み取ります(その後、無視します)。

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

于 2008-11-25T19:01:22.387 に答える
45

スローダウンは、必ずしもどのコンパイラでも同じではありません。

私は Delphi や Kylix を使用したことはありませんが、MS-DOS の時代には、Turbo Pascal プログラムはほぼ瞬時にコンパイルされましたが、同等の Turbo C++ プログラムはクロールするだけでした。

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

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

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

于 2008-11-25T18:55:04.630 に答える
18

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

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

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

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

于 2008-11-25T18:28:21.157 に答える
18

もう 1 つの理由は、宣言を配置するために C プリプロセッサを使用していることです。ヘッダー ガードを使用しても、.h が含まれるたびに何度も解析する必要があります。一部のコンパイラは、これに役立つプリコンパイル済みヘッダーをサポートしていますが、常に使用されるとは限りません。

参照: C++ のよくある質問への回答

于 2008-11-25T18:32:37.937 に答える
16

C/C++ のビルド: 実際には何が起きているのか、なぜそんなに時間がかかるのか

ソフトウェア開発時間の比較的大きな部分は、コードの作成、実行、デバッグ、さらには設計に費やされず、コンパイルが完了するのを待っています。処理を高速化するには、まず C/C++ ソフトウェアのコンパイル時に何が起こっているかを理解する必要があります。手順はおおよそ次のとおりです。

  • 構成
  • ビルドツール起動
  • 依存関係のチェック
  • コンパイル
  • リンキング

ここでは、各ステップをどのように高速化できるかに焦点を当てて、より詳細に見ていきます。

構成

これは、ビルドを開始する最初のステップです。通常、構成スクリプトまたは CMake、Gyp、SCons、またはその他のツールを実行することを意味します。非常に大きな Autotools ベースの構成スクリプトの場合、これには 1 秒から数分かかることがあります。

このステップは比較的まれに発生します。構成を変更するとき、またはビルド構成を変更するときにのみ実行する必要があります。ビルド システムを変更する以外に、このステップを高速化するためにできることはあまりありません。

ビルドツール起動

これは、make を実行するか、IDE のビルド アイコン (通常は make のエイリアス) をクリックすると発生します。ビルド ツールのバイナリが起動し、その構成ファイルとビルド構成を読み取ります。これらは通常、同じものです。

ビルドの複雑さとサイズに応じて、これには数分の 1 秒から数秒かかる場合があります。これ自体はそれほど悪くないでしょう。残念なことに、ほとんどの make ベースのビルド システムでは、ビルドごとに make が数十回から数百回呼び出されます。通常、これは make の再帰的な使用によって引き起こされます (これは悪いことです)。

Make が非常に遅い理由は、実装上のバグではないことに注意してください。Makefile の構文には、非常に高速な実装をほとんど不可能にするいくつかの癖があります。この問題は、次のステップと組み合わせるとさらに顕著になります。

依存関係のチェック

ビルド ツールが構成を読み取った後、どのファイルが変更され、どのファイルを再コンパイルする必要があるかを判断する必要があります。構成ファイルには、ビルドの依存関係を説明する有向非巡回グラフが含まれています。このグラフは通常、構成ステップで作成されます。ビルド ツールの起動時間と依存関係スキャナーは、すべてのビルドで実行されます。これらを組み合わせた実行時間によって、編集-コンパイル-デバッグ サイクルの下限が決まります。小規模なプロジェクトの場合、この時間は通常数秒程度です。これは許容範囲です。Makeの代替手段があります。それらの中で最も速いのは、Google のエンジニアが Chromium 用に構築した Ninja です。ビルドに CMake または Gyp を使用している場合は、Ninja バックエンドに切り替えるだけです。ビルド ファイル自体を変更する必要はありません。高速化を楽しむだけです。Ninja はほとんどのディストリビューションにパッケージ化されていませんが、

コンパイル

この時点で、最終的にコンパイラを呼び出します。いくつかのコーナーを切り取り、ここで実行されるおおよその手順を示します。

  • マージには以下が含まれます
  • コードの解析
  • コード生成/最適化

一般に信じられていることとは反対に、C++ のコンパイルは実際にはそれほど遅くはありません。STL は遅く、C++ のコンパイルに使用されるほとんどのビルド ツールは低速です。ただし、言語の遅い部分を軽減するためのより高速なツールと方法があります。

それらを使用するには、多少のエルボー グリースが必要ですが、その利点は否定できません。ビルド時間の短縮は、開発者の満足度の向上、俊敏性の向上、そして最終的にはより優れたコードにつながります。

于 2015-04-23T15:30:26.420 に答える
15

最大の問題は次のとおりです。

1) 無限ヘッダーの再解析。すでに述べた。軽減策 (#pragma once など) は通常、ビルドごとではなく、コンパイル ユニットごとにのみ機能します。

2) 多くの場合、ツールチェーンは複数のバイナリ (メイク、プリプロセッサ、コンパイラ、アセンブラ、アーカイバ、impdef、リンカー、および dlltool) に分割されることが多く、呼び出しごとに常にすべての状態を再初期化およびリロードする必要があります (コンパイラ、アセンブラ) またはいくつかのファイル (アーカイバ、リンカ、および dlltool) ごとに。

comp.compilers に関するこの議論も参照してください: http://compilers.iecc.com/comparch/article/03-11-078特にこれ:

http://compilers.iecc.com/comparch/article/02-07-128

comp.compilers のモデレーターである John も同意しているようで、これは、ツールチェーンを完全に統合し、プリコンパイル済みヘッダーを実装すれば、C でも同様の速度を達成できるはずであることを意味することに注意してください。多くの商用 C コンパイラは、これをある程度行っています。

すべてを個別のバイナリに分解する Unix モデルは、Windows にとって最悪のケースのモデルであることに注意してください (プロセスの作成が遅いため)。Windows と *nix の間で GCC のビルド時間を比較すると、特に make/configure システムが情報を取得するためだけにいくつかのプログラムを呼び出す場合に、非常に顕著です。

于 2009-05-02T11:30:47.243 に答える
12

いくつかの理由は次のとおりです。

1) C++ の文法は C# や Java よりも複雑で、解析に時間がかかります。

2) (より重要) C++ コンパイラはマシン コードを生成し、コンパイル中にすべての最適化を行います。C# と Java は道半ばであり、これらの手順は JIT に任せます。

于 2008-11-25T18:27:36.730 に答える
11

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

#include "BigClass.h"

class SmallClass
{
   BigClass m_bigClass;
}

以下よりもかなり遅いコンパイル:

class BigClass;

class SmallClass
{
   BigClass* m_bigClass;
}
于 2008-11-25T18:33:53.283 に答える
9

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

たとえば、a.cpp、b.cpp、および c.cpp.. ファイルを作成するとします。

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

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

于 2013-03-03T22:35:22.540 に答える
6

得られるトレードオフは、プログラムが少し速く実行されることです。これは、開発中は何の慰めにもならないかもしれませんが、開発が完了し、プログラムがユーザーによって実行されるようになると、非常に重要になる可能性があります。

于 2008-12-31T15:08:32.133 に答える
5

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

考えられる問題 #1 - ヘッダーのコンパイル: (これは、別の回答またはコメントによって既に対処されている場合とされていない場合があります。) Microsoft Visual C++ (別名 VC++) はプリコンパイル済みヘッダーをサポートしており、これを強くお勧めします。新しいプロジェクトを作成し、作成するプログラムのタイプを選択すると、セットアップ ウィザード ウィンドウが画面に表示されます。その下部にある「次へ >」ボタンを押すと、ウィンドウは機能のいくつかのリストを含むページに移動します。「プリコンパイル済みヘッダー」オプションの横にあるボックスがチェックされていることを確認してください。(注: これは C++ の Win32 コンソール アプリケーションでの私の経験ですが、C++ のすべての種類のプログラムには当てはまらない場合があります。)

考えられる問題 2 - コンパイル先の場所:この夏、私はプログラミング コースを受講し、すべてのプロジェクトを 8GB のフラッシュ ドライブに保存する必要がありました。これは、使用していた研究室のコンピューターが毎晩真夜中に消去されるためです。それは私たちの仕事をすべて消し去っていたでしょう。移植性/セキュリティなどのために外部ストレージ デバイスにコンパイルする場合、非常に長い時間がかかる可能性があります。特にかなり大きなプログラムの場合は、プログラムがコンパイルされるまでの時間 (前述のプリコンパイル済みヘッダーを使用した場合でも)。この場合の私のアドバイスは、使用しているコンピューターのハードドライブでプログラムを作成してコンパイルし、何らかの理由でプロジェクトの作業を停止したい/停止する必要があるときはいつでも、それらを外部に転送することです. 「ハードウェアを安全に取り外してメディアを取り出す」アイコンをクリックします。このアイコンは、白いチェック マークが付いた小さな緑色の円の後ろに小さなフラッシュ ドライブとして表示されます。

これがお役に立てば幸いです。もしそうなら教えてください!:)

于 2016-08-18T02:11:17.270 に答える