1

次のタイプの何百万ものクラスを初期化しています

template<class T>
struct node
{
  //some functions
private:
  T m_data_1;
  T m_data_2;
  T m_data_3;

  node* m_parent_1;
  node* m_parent_2;
  node* m_child;
}

floatテンプレートの目的は、ユーザーが精度を選択できるようにすることです。これは、使用するメモリ (RAM) が少なくなるdoubleという考えによるものです。node<float>

doubleただし、 からに切り替えてfloatも、プログラムのメモリ フットプリントが期待どおりに減少しません。質問が 2 つあります。

  1. コンパイラ/オペレーティング システムが、float に必要なスペースよりも多くのスペースを予約している可能性はありますか (または double として格納することさえあります)。もしそうなら、どうすればこれを止めることができますか - 私はg ++を搭載した64ビットマシンでLinuxを使用しています。

  2. すべての異なるクラスで使用されているメモリの量を判断できるツールはありますか? (つまり、ある種のメモリプロファイリング)-メモリが、私が考えていなかった他の場所でグブリングされていないことを確認するため。

4

3 に答える 3

5

64 ビット用にコンパイルしている場合、各ポインターのサイズは 64 ビットになります。これは、64 ビットに揃える必要がある場合があることも意味します。したがって、3 つの float を格納する場合、4 バイトのパディングを挿入する必要がある場合があります。したがって、12 バイトを節約する代わりに、8 バイトしか節約できません。ポインターが構造体の先頭にあるか末尾にあるかにかかわらず、パディングは引き続き存在します。これは、連続した構造体を配列に配置してアライメントを維持し続けるために必要です。

また、構造は主に 3 つのポインターで構成されています。保存した 8 バイトによって、48 バイトのオブジェクトから 40 バイトのオブジェクトになります。それは正確には大幅な減少ではありません。繰り返しますが、64 ビット用にコンパイルしている場合です。

32 ビット用にコンパイルしている場合は、36 バイト構造から 12 バイトを節約していることになります。double を 8 バイトに揃える必要がある場合は、さらに多くなる可能性があります。

于 2011-08-01T01:15:01.237 に答える
3

他の答えは、不一致の原因について正しいです。ただし、x86 / x86-64上のポインター(およびその他のタイプ)は、整列する必要はありません。GCCがデフォルトでそれらを調整し続ける理由は、パフォーマンスが優れているということだけです。

ただし、GCCには、これを制御できる「パック」属性が用意されています。

#include <iostream>

template<class T>
struct node
{
private:
    T m_data_1;
    T m_data_2;
    T m_data_3;

    node* m_parent_1;
    node* m_parent_2;
    node* m_child;
}    ;

template<class T>
struct node2
{
private:
    T m_data_1;
    T m_data_2;
    T m_data_3;

    node2* m_parent_1;
    node2* m_parent_2;
    node2* m_child;
} __attribute__((packed));

int
main(int argc, char *argv[])
{
    std::cout << "sizeof(node<double>) == " << sizeof(node<double>) << std::endl;
    std::cout << "sizeof(node<float>) == " << sizeof(node<float>) << std::endl;
    std::cout << "sizeof(node2<float>) == " << sizeof(node2<float>) << std::endl;
    return 0;
}

私のシステム(x86-64、g ++ 4.5.2)では、このプログラムは次のように出力します。

sizeof(node<double>) == 48
sizeof(node<float>) == 40
sizeof(node2<float>) == 36

もちろん、「属性」メカニズムと「パック」属性自体はGCC固有です。

于 2011-08-01T03:39:47.047 に答える
1

ニコルが作る有効なポイントに加えて:

new / mallocを呼び出す場合、メモリを割り当てるためのOSの呼び出しと必ずしも1対1で対応するとは限りません。これは、高価なシステム呼び出しの数を減らすために、ヒープマネージャーが要求された以上の量を割り当て、new/mallocを呼び出すときにそのチャンクを「サブ割り当て」する場合があるためです。また、メモリは一度に4kbしか割り当てることができません(通常、これは最小ページサイズです)。基本的に、将来の割り当てを高速化するために、現在アクティブに使用されていないメモリのチャンクが割り当てられている可能性があります。

質問に直接答えるには:

1)はい、ランタイムはあなたが要求したよりも多くのメモリを割り当てる可能性が非常に高いです-しかし、このメモリは無駄ではなく、将来のニュース/マロックに使用されますが、「タスクマネージャ」または使用するツールには引き続き表示されます。いいえ、フロートをダブルに昇格させることはありません。割り当てる割り当てが多いほど、このエッジ状態がサイズの違いの原因になる可能性は低くなり、ニコルのアイテムが優勢になります。割り当ての数が少ない場合は、このアイテムが優勢になる可能性があります(「大」と「小」は、OSとカーネルに完全に依存します)。

2)Windowsタスクマネージャは、割り当てられた合計メモリを提供します。WinDbgのようなものは、実行時に割り当てられた仮想メモリ範囲チャンク(通常はツリーに割り当てられます)を実際に提供します。Linuxの場合、このデータは、プロセスに関連付けられた/procディレクトリ内のファイルの1つで利用できると思います。

于 2011-08-01T03:26:48.613 に答える