C ++の初期割り当てがCの割り当てよりもはるかに大きいのはなぜですか?


138

同じコードを使用する場合、コンパイラーを(CコンパイラーからC ++コンパイラーに)変更するだけで、割り当てられるメモリの量が変わります。これがなぜなのかよくわからないので、もっと理解したいと思います。これまでのところ、私が得た最良の応答は「おそらくI / Oストリーム」です。これはあまり説明的ではなく、C ++の「使用しないものにお金を払わない」という側面について疑問に思います。

ClangコンパイラとGCCコンパイラのバージョン7.0.1-8と8.3.0-6をそれぞれ使用しています。私のシステムは最新のDebian 10(Buster)で実行されています。ベンチマークはValgrind Massifを介して行われます。

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

使用されるコードは変更されませんが、CまたはC ++のどちらでコンパイルしても、Valgrindベンチマークの結果が変更されます。ただし、値はコンパイラ間で一貫しています。プログラムの実行時割り当て(ピーク)は次のようになります。

  • GCC(C):1,032バイト(1 KB)
  • G ++(C ++):73,744バイト(〜74 KB)
  • Clang(C):1,032バイト(1 KB)
  • Clang ++(C ++):73,744バイト(〜74 KB)

コンパイルには、次のコマンドを使用します。

clang -O3 -o c-clang ./main.c
gcc -O3 -o c-gcc ./main.c
clang++ -O3 -o cpp-clang ./main.cpp
g++ -O3 -o cpp-gcc ./main.cpp

Valgrindの場合、valgrind --tool=massif --massif-out-file=m_compiler_lang ./compiler-lang各コンパイラーと言語で実行してからms_print、ピークを表示します。

ここで何か悪いことをしていますか?


11
まず、どのように構築していますか?どのオプションを使用していますか?そして、どのように測定しますか?Valgrindをどのように実行しますか?
一部のプログラマーの男

17
私が正しく覚えているとしたら、最近のC ++コンパイラーはtry、おそらくジャンプテーブルなどを使用して、より大きなメモリーフットプリントを犠牲にしてブロックに入るパフォーマンスヒットがない例外モデルでなければなりません。例外なくコンパイルしてみて、それがどのような影響を与えるかを確認してください。編集:実際、さまざまなc ++機能を繰り返し無効にして、メモリフットプリントにどのような影響があるかを確認します。
フランソワアンドリュー

3
clang++ -xcではなくでコンパイルするとclang、同じ割り当てがありました。これは、リンクされたライブラリによるものであることを強く示唆しています
Justin

14
@bigwillydosこれは確かにC ++です。C++仕様の一部が壊れているのはわかりません... cstdioではなくstdio.hを含める可能性があることを除いて、これは少なくとも古いC ++バージョンで許可されています。このプログラムで「不正」とは何だと思いますか?
Vality

4
これらのgccコンパイラーとclangコンパイラーがCmodeでまったく同じバイト数とまったく同じバイト数モードを生成することは疑わしいと思いC++ます。文字起こしミスをしましたか?
RonJohn

回答:


149

ヒープ使用量は、C ++標準ライブラリから取得されます。起動時に内部ライブラリが使用するメモリを割り当てます。リンクしない場合は、CバージョンとC ++バージョンの間に差はありません。GCCとClangを使用すると、次のコマンドでファイルをコンパイルできます。

g ++ -Wl、-as-needed main.cpp

これは、未使用のライブラリにリンクしないようにリンカに指示します。コード例では、C ++ライブラリは使用されていないため、C ++標準ライブラリにリンクしないでください。

Cファイルを使用してこれをテストすることもできます。あなたがコンパイルした場合:

gcc main.c -lstdc ++

Cプログラムをビルドした場合でも、ヒープ使用量が再び表示されます。

ヒープの使用は明らかに、使用している特定のC ++ライブラリの実装に依存しています。あなたの場合、それはGNU C ++ライブラリlibstdc ++です。他の実装では、同じ量のメモリが割り当てられない場合や、メモリがまったく割り当てられない場合があります(少なくとも起動時ではありません)。たとえば、少なくとも私のLinuxでは、LLVM C ++ライブラリ(libc ++)は起動時にヒープ割り当てを行いません機械:

clang ++ -stdlib = libc ++ main.cpp

ヒープの使用は、まったくリンクしないのと同じです。

(コンパイルが失敗した場合、libc ++はおそらくインストールされていません。パッケージ名には通常「libc ++」または「libcxx」が含まれています。)


50
この答えを見たとき、私の最初の考えは、「このフラグが不要なオーバーヘッドを減らすのに役立つなら、なぜデフォルトでオンにならないのですか?」です。それに対する良い答えはありますか?
Nat

4
@Nat私の推測では、開発時にはコンパイルが遅くなります。リリースビルドを作成する準備ができたら、それをオンにします。また、標準/大規模なコードベースでは、違いはごくわずかである可能性があります(多くのSTDライブラリなどを使用している場合)
DarcyThomas

24
@Nat -Wl,--as-neededフラグは、-lフラグで指定したライブラリを削除しますが、実際には使用していません。したがって、ライブラリを使用しない場合は、リンクしないでください。これにはこのフラグは必要ありません。ただし、ビルドシステムが追加するライブラリが多すぎて、それらをすべてクリーンアップして必要なライブラリのみをリンクするのが面倒な場合は、代わりにこのフラグを使用できます。標準ライブラリは自動的にリンクされるため、例外です。したがって、これはコーナーケースです。
Nikos C.

36
@Nat --as-neededは望ましくない副作用をもたらす可能性があります。これは、ライブラリのシンボルを使用しているかどうかをチェックすることで機能し、テストに失敗したものを追い出します。ただし、ライブラリは暗黙的にさまざまな処理を実行することもできます。たとえば、ライブラリに静的C ++インスタンスがある場合、そのコンストラクタが自動的に呼び出されます。明示的に呼び出さないライブラリが必要になることはまれですが、それらは存在します。
Norbert Lange

3
@NikosC。ビルドシステムは、アプリケーションが使用するシンボル、およびそれらを実装するライブラリ(コンパイラ、アーチ、ディストリビューション、およびc / c ++ライブラリによって異なります)を自動的に認識しません。これを正しく行うことは、少なくともベースランタイムライブラリにとってはやっかいな問題です。ただし、まれにライブラリが必要な場合は、そのライブラリに--no-as-neededを使用し、他のすべての場所には--as-neededをそのままにしてください。私が見たユースケースは、トレース/デバッグ(lttng)用のライブラリと、一種の認証/接続を行うライブラリです。
Norbert Lange

16

GCCもClangもコンパイラではありません。これらは実際にはツールチェーンドライバプログラムです。つまり、コンパイラ、アセンブラ、リンカを呼び出します。

CまたはC ++コンパイラでコードをコンパイルすると、同じアセンブリが生成されます。アセンブラは同じオブジェクトを生成します。違いは、ツールチェーンドライバーが2つの異なる言語のリンカーに異なる入力を提供することです:異なるスタートアップ(C ++では、名前空間レベルで静的またはスレッドローカルストレージ期間を持つオブジェクトのコンストラクターとデストラクターを実行するためのコードが必要であり、スタックのインフラストラクチャが必要ですたとえば、例外処理中の巻き戻しをサポートするフレーム)、C ++標準ライブラリ(名前空間レベルで静的ストレージ期間のオブジェクトも含まれる)、およびおそらく追加のランタイムライブラリ(たとえば、スタック巻き戻しインフラストラクチャを備えたlibgcc)

要するに、フットプリントの増加を引き起こしているのはコンパイラではなく、C ++言語を選択して使用することを選択したもののリンクインです。

C ++には「使用した分だけ支払う」という哲学があることは事実ですが、言語を使用することで、その代金を支払うことになります。言語の一部(RTTI、例外処理)を無効にすることができますが、その場合はC ++を使用しなくなります。別の答えで述べたように、標準ライブラリをまったく使用しない場合は、ドライバーにそれを省略するように指示できます(--Wl、-as-needed)が、機能のいずれも使用しない場合C ++またはそのライブラリの場合、なぜC ++をプログラミング言語として選択するのですか?


例外処理を有効にすると、実際には使用しなくてもコストがかかるという問題があります。これはC ++機能では一般的ではなく、C ++標準ワーキンググループが修正方法を検討しようとしていることです。ACCU 2019で確認ハーブサッターの基調講演デフラグメンテーションC ++:例外がより手頃な価格で使いやすい作り。ただし、現在のC ++では、これは残念な事実です。また、従来のC ++例外は、新しい例外の新しいメカニズムがキーワードで追加された場合でも、おそらくそのコストが常にかかります。
Peter Cordes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.