我正在初始化数百万个以下类型的类
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
或double
精度,其想法是node<float>
占用更少的内存(RAM)。
但是,当我从double
切换到float
时,程序的内存占用量并没有像我预期的那样减少。我有两个问题,
编译器/操作系统是否可能为我的浮点数保留更多的空间(甚至将它们存储为双精度数)。如果是这样,我该如何阻止这种情况发生 - 我在使用g ++的64位机器上使用linux。
是否有工具可以让我确定所有不同类使用的内存量? (即某种内存分析) - 确保内存没有被我想到的其他地方搞砸。
答案 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目录中的一个文件中找到。