C ++内存对齐是正确还是低效?

时间:2012-11-13 23:32:38

标签: c++ memory alignment

我测试了这段代码,试图找出为新运算符实际保留了多少内存c ++。

#include<iostream>
using namespace std;
int main() {

  cout << "alignment of " << alignof(int) << endl;
  int *intP1 = new int;
  *intP1 = 100;
  cout << "address of intP1 " << intP1 << endl;
  int *intP2 = new int;
  *intP2 = 999;
  cout << "address of intP2 " << intP2 << endl;
  int *intP3 = new int;
  cout << "address of intP3 " << intP3 << endl;
  *intP3 = 333;

  cout << endl;
  cout << (reinterpret_cast<char *>(intP3)-reinterpret_cast<char *>(intP2)) << endl;
  cout << intP3-intP2 << endl;
  cout << endl;

  cout << *(intP1) << endl;
  cout << *(intP1+4) << endl;
  cout << *(intP1+8) << endl;
  cout << *(intP1+16) << endl;
  delete intP1;
  delete intP2;
  delete intP3;
  return 0;
}

使用-std = c ++ 11标志编译代码并运行它之后,这是我从x86_64机器获得的。

    alignment of int4
    address of intP1 = 0xa59010
    address of intP2 = 0xa59030
    address of intP3 = 0xa59050

    the distance of intP3 and intP2 = 32

    intP1 value = 100
    is this a padding value = 0
    intP2 value = 999
    intP3 value = 333

似乎当使用new为整数分配4字节内存时,它实际上保留了32字节块,这是8个整数的总空间。根据c ++对齐的解释,对于64位机器,内存对齐16个字节,为什么这里的距离是32个字节?

有人可以帮我解决这个问题吗?提前致谢。

7 个答案:

答案 0 :(得分:5)

它与对齐无关 - 它是内部内存分配器工作方式的额外开销。通常,每个内存块在其前面和/或后面都有额外的隐藏信息,用于维护堆的结构。究竟有多少开销会因平台和实现而异。

例如,Doug Lea's malloc每个分配(32位指针)或8-16个字节(64位指针)的额外开销为4-8个字节,最小分配大小为16个字节(32-位)或32字节(64位)。这意味着即使是1字节的分配,内存分配器也需要总共16个字节的跟踪开销。

答案 1 :(得分:2)

32字节差异仅用于对齐。实际上,请注意地址0xa59010不是32对齐的,它只是16对齐的。因此,如果地址只有16个字节而不是32个,那么地址的对齐就不会更糟。

相反,32字节差异是内存分配器的开销/低效率。我怀疑是分配器:

  • 有助于为您提供16个对齐的地址。这就是128位SSE类型所需要的,所以它对你很有用,但我不知道这是否是分配器16对齐的主要原因,还是它对分配器来说是否方便。
  • 在预订信息的分配之前需要一些空间,这可能是16个字节(2个指针或指针和大小),但即使不是,它也会被四舍五入到16个字节。
  • 只需要4个字节用于实际数据,但由于16个字节的簿记和16字节对齐,分配之间的最小距离为32个字节。因此,当你进行4字节分配时,有12个字节的“松弛空间”/“内部碎片”/“浪费”。

但这只是一个猜测,我没有研究过你正在使用的分配器。

答案 2 :(得分:1)

new的调试版本可以添加相当多的填充以提供保护空间,从而可以检测到一些堆损坏。您应该使用调试和发布版本来运行它,看看是否存在差异。

答案 3 :(得分:0)

您同时拥有指针和内存值。这分配了2个内存块,你已经说过每块16个字节。的2x16 = 32

我相信这会给你你想要的结果。

  cout << "alignment of " << alignof(int) << endl;
  int intP1 = 100;
  cout << "address of intP1 " << &intP1 << endl;
  int intP2 = 999;
  cout << "address of intP2 " << &intP2 << endl;
  int intP3 = 333;
  cout << "address of intP3 " << &intP3 << endl;

答案 4 :(得分:0)

int4表示它占用4个字节,而不是4个字节。编译器向您显示的所有内容实际上都是准确的! Here是关于基元的一些文档以及int基元的含义。

Here是关于如何定义4位整数的教程。

我要提一下alignof依赖于架构。在某些体系结构中int表示16位int或2字节而不是4。

答案 5 :(得分:0)

绝对不需要操作系统将三个指针intP1intP2intP3分配到彼此。您的代码可能正在检测分配中的开销(当然,这是一个合理的假设,但有一些),但这不足以证明间距必然是所有分配器开销。

答案 6 :(得分:0)

C ++标准不保证每个堆分配有多少开销。无论是否对齐,分配器通常都会增加额外的开销。 分配器通常使用预先确定的桶来实现小分配。这里看起来最小的桶是每个分配32个字节,这并不罕见。你很少会在野外找到小于16字节的分配器。

如果要分配多于1个int,比如int [2],您可能会注意到所占用的内存大小是相同的:32个字节。

另请注意,C ++标准或分配器无法保证相同大小的2个分配是连续的。这可能在大多数时候都得到尊重,但不应该依赖。