web-dev-qa-db-ja.com

ヘッダーのみのライブラリの方が効率的ですか?

仮定

  1. C++のヘッダーのみのライブラリの利点の1つは、ライブラリを個別にコンパイルする必要がないことです。

  2. CおよびC++では、inlineは、ヘッダーファイルで関数がdefinedの場合にのみ意味があります*。

  3. 伝統的に、Cでは.c/.hレイアウトが使用されており、ヘッダーは翻訳単位の最小限のパブリックインターフェイスを表します。同様に、.cpp/hpp。

質問

ヘッダーのみのライブラリは一般的に、従来のレイアウトよりもコードと実行時間の面で効率的ですか?もしそうなら、これは広範なインライン化または他の最適化によるものですか?

*-ヘッダーで関数を定義すると、コンパイラーは変換ユニットのコンパイル中に実装を確認でき、コードのインライン化が事実上可能になります

51
Vorac

C++のヘッダーのみのライブラリの利点の1つは、個別にコンパイルする必要がないことです。

いいえ、それは利点ではありません。まったく逆です。ライブラリの主要部分は、一度だけではなく、組み込まれるたびにコンパイルする必要があります。通常は、コンパイル時間が長くなります。ただし、リストされている利点について言及している場合 ここではWikipedia :この記事は、ビルド、パッケージ化、およびデプロイメントプロセス全体に関する管理オーバーヘッドの減少について述べています。

CおよびC++のインラインでは、関数がヘッダーファイルで定義されている場合にのみ意味があります*

これはコンパイラ/リンカーシステムに依存しますが、既存のほとんどのCおよびC++コンパイラではこれは当てはまると思います。

伝統的に、Cでは.c/.hレイアウトが使用されており、ヘッダーは翻訳単位の最小限のパブリックインターフェイスを表します。同様に、.cpp/hpp。

それはほとんど正しいです。 C++クラスヘッダーには、最小限のパブリックインターフェイス以上のものが含まれていることがよくあります。通常、これらには、多くのプライベートなものも含まれています。これを緩和するために、 PIMPLイディオム のようなものが使用されます。これは、ヘッダーのみのライブラリの「反対」のようなもので、必要なヘッダーコンテンツを最小限に抑えようとします。

しかし、あなたの主な質問に答えるには、これはトレードオフです。ヘッダーファイルに追加するライブラリコードが多いほど、コンパイラーは速度を上げるためにコードを最適化する機会が多くなります(これが実際に発生する場合、または増加は注目に値しますが、完全に異なる質問です)。一方、ヘッダーのコードが多すぎると、コンパイル時間が長くなります。特に大規模なC++プロジェクトでは、これは深刻な問題になる可能性があります。 "Large Scale C++ Software Design" by John Lakos を参照してください。本は少し古く、そこで説明されている問題のいくつかは、現代のコンパイラ、一般的なアイデア/ソリューションはまだ有効です。

特に、安定した(サードパーティの)ライブラリを使用していないが、プロジェクト中に独自のライブラリを開発している場合、コンパイル時間が明らかになります。 libで何かを変更するたびに、ヘッダーファイルを変更する必要があります。これにより、すべての依存ユニットの再コンパイルとリンケージが発生します。

IMHOヘッダーのみのライブラリの人気は、テンプレートメタプログラミングの人気が原因です。ほとんどのコンパイラーでは、テンプレート化されたライブラリmustはヘッダーのみである必要があります。コンパイラーは、型パラメーターが提供されたときにのみメインコンパイルプロセスを開始できるためです。完全なコンパイルと最適化のために、コンパイラーは「一度に両方」を参照する必要があります。 -ライブラリコードとテンプレートパラメータ値。そのため、そのようなライブラリの「プリコンパイル済み」コンパイルユニットを作成することは不可能(または少なくとも困難)になります。

48
Doc Brown

さて、まずあなたの仮定のいくつかを解体しましょう:

  1. C++のヘッダーのみのライブラリの利点の1つは、個別にコンパイルする必要がないことです。

別々にコンパイルすることは、一部のみが変更された場合にすべてを再コンパイルする必要がないことを意味します。
したがって、利点ではなく欠点です。

  1. CおよびC++では、関数がヘッダーファイル*で定義されている場合にのみインラインが意味をなします。

はい、inlineが残した唯一の影響は one-definition-rule の例外です。
しかし、それらの定義が何らかの点で異なっている場合、あなたには悲惨です。

そのため、関数がコンパイル単位の内部にある場合は、staticとマークします。また、インライン化するために関数が使用可能である必要があるため、インライン化がより可能になります。
それでも、少なくともMSVC++、gcc、およびclangでサポートされているリンク時最適化を確認してください。

  1. 伝統的に、Cでは.c/.hレイアウトが使用され、ヘッダーは翻訳単位の最小限のパブリックインターフェイスを表します。同様に、.cpp/hpp。

まあ、最小限のインターフェースを提示することだけが、高いAPIとABIの安定性を実現し、コンパイル時間を最小限にするという目標の1つであることは確かです。

特にC++クラスは実際にはそれに適合していません。すべてのプライベートビットがヘッダーにリークするため、そこから派生するかどうかにかかわらず、保護されたビットはヘッダーにリークします。

デザインパターン [〜#〜] pimpl [〜#〜] は、そのような詳細を減らすためのものです。

ただし、C++でインターフェイスと実装の分離が完全に失敗する部分はテンプレートです。
委員会は エクスポートされたテンプレート を使って何かを行おうとしましたが、それはあまりに複雑で、実際には機能しないため放棄されました。

現在、彼らは 適切なモジュールシステム に取り組んでいますが、遅いです。これにより、コンパイル時間が大幅に短縮され、表面を減らすことでAPIとABIの安定性も向上します。

ヘッダーのみのライブラリは一般的に、従来のレイアウトよりもコードと実行時間の面で効率的ですか?もしそうなら、これは広範なインライン化または他の最適化によるものですか?

ヘッダーのみのライブラリは、ライブラリが共有されているかどうか、ライブラリがどの程度使用されているか、どのように使用されているか、およびインライン化が特定のケースで決定的な勝利をもたらすかどうかによって異なりますが、コードサイズと実行時間においてより効率的です。

最適化にとってインライン化が非常に重要である理由は、インライン化自体が非常に優れているためではなく、一定の伝播とさらなる最適化の機会があるためです。

16
Deduplicator