类中的内存使用 - 将double转换为float并没有像预期的那样减少内存使用量

时间:2011-08-01 01:10:00

标签: c++ memory-management memory-profiling

我正在初始化数百万个以下类型的类

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)。

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

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

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

3 个答案:

答案 0 :(得分:5)

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

此外,您的结构主要由3个指针组成。保存的8个字节将您从48字节对象转移到40字节对象。这并不是一个大幅下降。再次,如果你正在为64位编译。

如果您正在编译32位,那么您将从36字节结构中保存12个字节,这在百分比方面更好。如果双精度必须与8个字节对齐,则可能更多。

答案 1 :(得分: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特定的。

答案 2 :(得分:1)

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

当你调用new / malloc时,调用OS来分配内存时,它不一定与1对1对应。这是因为为了减少昂贵的系统调用次数,堆管理器可能会分配超过请求的数量,然后在调用new / malloc时“子分配”其中的块。此外,内存一次只能分配4kb(通常 - 这是最小页面大小)。实质上,可能存在当前未被主动使用的内存块,以便加速将来的分配。

直接回答您的问题:

1)是的,运行时很可能会分配更多内存然后你要求 - 但是这个内存没有浪费,它将用于未来的新闻/ mallocs,但仍然会出现在“任务管理器”或任何工具中你用。不,它不会促进浮动到双打。你做出的分配越多,这种边缘条件就越不可能成为尺寸差异的原因,而尼科尔的项目将占主导地位。对于较少数量的分配,此项可能占主导地位(“大”和“小”完全取决于您的操作系统和内核)。

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