【问题标题】:Memory Alignment in C/C++C/C++ 中的内存对齐
【发布时间】:2017-06-02 20:33:37
【问题描述】:

我正在阅读 Game Coding Complete 第 4 版。有一个关于内存对齐的话题。在下面的代码中,作者说第一个结构非常慢,因为它既不是位对齐也不是字节对齐。第二个不是位对齐的,而是字节对齐的。最后一个很快,因为两者兼而有之。他说如果没有编译指示,编译器会对齐内存本身,这会导致内存浪费。我真的无法得到计算。

这是文本的一部分:-

如果让编译器通过添加未使用的来优化 SlowStruct 字节,每个结构将是 24 个字节,而不是只有 14 个字节。 七 在第一个 char 变量之后填充额外的字节,并且 剩余的字节被添加到最后。这确保了整个 结构总是从 8 字节边界开始。这大约是 40% 浪费的空间,都是由于成员变量的粗心排序造成的。

这是以粗体显示的结论:-

不要让编译器浪费宝贵的内存空间。把你的一些 脑细胞工作并调整您自己的成员变量。

请告诉我计算并更清楚地解释填充概念。

代码:-

#pragma pack(push, 1)
struct ReallySlowStruct
{
    char c : 6;
    __int64 d : 64;
    int b : 32;
    char a : 8;
};

struct SlowStruct
{
    char c;
    __int64 d;
    int b;
    char a;
};

struct FastStruct
{
   __int64 d;
   __int b;
   char a;
   char c;
   char unused[2];
};
#pragma pack(pop)

【问题讨论】:

标签: c++ c memory memory-management


【解决方案1】:

书中给出的示例高度依赖于使用的编译器和计算机体系结构。如果您在自己的程序中测试它们,您可能会得到与作者完全不同的结果。我将假设一个 64 位架构,因为作者也是,从我在描述中读到的。 让我们一个一个看例子:

ReallySlowStruct 如果使用的编译器支持非字节对齐的结构成员,“d”的开头将位于结构第一个字节的第七位。听起来非常适合节省内存。问题在于,C 不允许位寻址。因此,要将 newValue 保存到“d”成员,编译器必须执行大量位移操作:将“newValue”的前两位保存在 byte0 中,向右移动 6 位。然后将“newValue”向左移动两位并从字节 1 开始保存。字节 1 是未对齐的内存位置,这意味着批量内存传输指令将不起作用,编译器必须一次保存每个字节。

SlowStruct 它变得更好了。编译器可以摆脱所有的位摆弄。但是写入“d”仍然需要一次写入每个字节,因为它未与本机“int”大小对齐。 64 位系统上的本机大小是 8。因此每个不能被 8 整除的内存地址一次只能访问一个字节。更糟糕的是,如果我关闭打包,我将浪费大量内存空间:每个成员后面跟着一个 int 将被填充足够的字节,让整数从可被 8 整除的内存位置开始。在这种情况下:char a 和 c 都将占用 8 个字节。

FastStruct 这与目标机器上的 int 大小对齐。 “d”应该占用 8 个字节。因为字符都捆绑在一个地方,编译器不会填充它们,也不会浪费空间。每个字符只有 1 个字节,所以我们不需要填充它们。完整的结构加起来总大小为 16 个字节。可被 8 整除,因此不需要填充。


在大多数情况下,您不必担心对齐,因为默认对齐已经是最佳的。但是,在某些情况下,您可以通过为数据结构指定自定义对齐方式来显着提高性能或节省内存。

在内存空间方面,编译器以自然对齐结构每个元素的方式填充结构。

struct x_
{
   char a;     // 1 byte
   int b;      // 4 bytes
   short c;    // 2 bytes
   char d;     // 1 byte
} bar[3];

struct x_ 被编译器填充,因此变为:

// Shows the actual memory layout
struct x_
{
   char a;           // 1 byte
   char _pad0[3];    // padding to put 'b' on 4-byte boundary
   int b;            // 4 bytes
   short c;          // 2 bytes
   char d;           // 1 byte
   char _pad1[1];    // padding to make sizeof(x_) multiple of 4
} bar[3];

来源:https://docs.microsoft.com/en-us/cpp/cpp/alignment-cpp-declarations?view=vs-2019

【讨论】:

  • 感谢您回答我的问题。刚才我从不同的链接运行了一些代码,并且在书中给出了这些代码并运行了实验,我清楚地知道没有决定填充的特定标准。但是作者暗示要以适当的方式了解您的编译器填充规则和顺序声明以提高性能。你的回答也非常有帮助。谢谢。
  • 我在 FastStruct 代码中犯了错误。我忘了添加 64 int var。对不起。请相应地更新答案。
  • 感谢您的更正回答。感谢您的帮助,现在我确信我已经理解了这个话题。再次感谢。
  • 在您对 FastStruct 的解释中,您谈到“b”占用 8 个字节。那不应该是“d”吗?
猜你喜欢
  • 2012-11-13
  • 2015-09-24
  • 2010-12-29
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
  • 1970-01-01
相关资源
最近更新 更多