类中的内存使用情况 - 将双精度转换为浮点数并未按预期减少内存使用量

memory usage of in class - converting double to float didn't reduce memory usage as expected

本文关键字:内存 浮点数 使用量 转换 用情 情况 双精度      更新时间:2023-10-16

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

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

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

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

同样,你的结构主要由3个指针组成。您节省的8个字节将您从一个48字节的对象变成一个40字节的对象。这并不是一个很大的下降。同样,如果您正在编译64位。

如果你编译为32位,那么你从一个36字节的结构中节省了12个字节,这是更好的百分比。如果double必须对齐为8字节,则可能更多。

关于差异的来源,其他答案都是正确的。然而,x86/x86-64上的指针(和其他类型)是不需要对齐的。只是当它们对齐时性能会更好,这就是为什么GCC默认保持它们对齐的原因。

但是GCC提供了一个"packed"属性来让你控制它:

#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

当然,"attribute"机制和"packed"属性本身是gcc特有的。

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

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

直接回答你的问题:

1)是的,运行时很可能会分配更多的内存,然后你要求-但这些内存不会浪费,它将用于未来的新闻/malloc,但仍然会出现在"任务管理器"或任何你使用的工具。不,它不会将float提升为double。你做的分配越多,这种边缘条件就越不可能导致尺寸差异,而Nicol的项目将占主导地位。对于较小数量的分配,该项可能占主导地位(其中"大"answers"小"完全取决于您的操作系统和内核)。

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