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;
}

模板的目的是使用户能够选择floatdouble精确,其想法是node<float>占用更少的内存(RAM)。

但是,当我切换doublefloat我的程序的内存占用时,它并没有像我预期的那样减少。我有两个问题,

  1. 编译器/操作系统是否有可能保留比我的浮点数所需的更多空间(甚至将它们存储为双精度)。如果是这样,我该如何阻止这种情况发生 - 我在 64 位机器上使用带有 g++ 的 linux。

  2. 是否有一个工具可以让我确定所有不同类使用的内存量?(即某种内存分析) - 以确保内存没有被我没有想到的其他地方吞噬。

4

3 回答 3

5

如果您正在编译 64 位,那么每个指针的大小都是 64 位。这也意味着它们可能需要对齐到 64 位。因此,如果您存储 3 个浮点数,则可能必须插入 4 个字节的填充。因此,您只需节省 8 个字节,而不是节省 12 个字节。无论指针位于结构的开头还是结尾,填充仍然存在。为了将连续的结构放入数组中以继续保持对齐,这是必要的。

此外,您的结构主要由 3 个指针组成。您保存的 8 个字节将您从 48 字节对象变为 40 字节对象。这并不完全是大幅下降。同样,如果您正在编译 64 位。

如果您正在编译 32 位,那么您将从 36 字节结构中节省 12 个字节,这在百分比方面更好。如果双精度必须对齐到 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

除了 Nicol 提出的有效观点之外:

当您调用 new/malloc 时,它不一定与调用操作系统分配内存一一对应。这是因为为了减少昂贵的系统调用次数,堆管理器可能会分配比请求更多的内存,然后在调用 new/malloc 时“子分配”其中的块。此外,一次只能分配 4kb 内存(通常 - 这是最小页面大小)。本质上,可能有分配的内存块当前没有被积极使用,以加速未来的分配。

直接回答您的问题:

1) 是的,运行时很可能会分配比您要求的更多的内存 - 但此内存不会浪费,它将用于未来的新闻/malloc,但仍会显示在“任务管理器”或您使用的任何工具中。不,它不会将花车提升为双打。您进行的分配越多,这种边缘条件就越不可能导致大小差异,并且 Nicol's 中的项目将占主导地位。对于较少数量的分配,此项可能占主导地位(其中“大”和“小”完全取决于您的操作系统和内核)。

2)windows任务管理器会给你分配的总内存。像 WinDbg 这样的东西实际上会给你运行时分配的虚拟内存范围块(通常在树中分配)。对于 Linux,我希望这些数据将在与您的进程关联的 /proc 目录中的一个文件中可用。

于 2011-08-01T03:26:48.613 回答