为什么编译器在N字节边界上对齐N字节数据类型?

时间:2014-09-12 10:35:43

标签: c memory compiler-construction memory-alignment

我不明白为什么编译器在4字节边界上对齐int,在2字节边界上对齐并在1字节边界上对char进行校对。 据我所知,如果处理器的数据总线宽度为4个字节,则从地址而不是4的倍数读取一个int需要2个内存读取周期。
那么,为什么编译器没有对齐4字节边界上的所有数据呢? 例如:

struct s {
 char c;
 short s;
};

下面, 1)为什么编译器在2字节边界上对齐?假设处理器可以在单个存储器读取周期中获取4个字节,即使在char和short之间没有填充,在上述情况下也只需要1个存储器读取周期来读取短路?

2)为什么编译器不能在4字节边界上对齐?

4 个答案:

答案 0 :(得分:3)

这些对象必须适合数组。数组是连续的。因此,如果第一个元素是N字节对齐,并且所有对象都是N字节大,那么数组中的所有对象也必须N字节对齐。

因此,如果short大2字节,但是4字节对齐,则数组中所有短路之间将有2个字节的空洞被禁止。

您确实看到您的假设存在轻微缺陷。我可以创建一个带有26个字符的struct,它不会是26字节对齐的。它可以从任何地方开始N字节类型,其对齐等于N 或除N

答案 1 :(得分:2)

首先,你的前提是不正确的。每个对象在一些基本对齐时对齐。对于某些标量对象,对齐可能与对象的数据大小相同,但也可能更小或更大。例如,一个经典的32位架构(我在这里考虑i386)可能包括8字节双精度和10字节长双精度,两者都有4字节对齐。请注意,我说上面的数据大小;不要将此与sizeof混淆。

对象的实际大小可能大于数据大小,因为对象的大小必须是对象对齐的倍数。原因是无论上下文如何,对象的对齐始终是相同的。换句话说,对象的对齐仅取决于对象的类型

因此,在结构中:

struct example1 {
  type1 a;
  type2 b;
};

struct example2 {
  type2 b;
  type1 a;
};

两个b的对齐方式是相同的。为了能够保证这种对齐,复合类型的对齐必须是成员类型的最大对齐。这意味着上面的struct example1struct example2具有相同的对齐方式。

对象的对齐仅依赖于其类型的要求意味着类型的大小必须是其对齐的倍数。 (任何类型都可以是数组的元素类型,包括只有一个元素的数组。数组的大小是元素大小和元素数量的乘积。所以任何填充必须是大小的一部分元素。)

通常,复合类型中的重新排列成员可能会更改复合类型的大小,但不能更改复合类型的对齐方式。例如,以下两个结构具有相同的对齐 - 这是double的对齐 - 但第一个结构几乎肯定更小:

struct compact {
  double d;   // Must be at offset 0
  char   c1;  // Will be at offset sizeof(double)
  char   c2;  // Will be at offset sizeof(double)+sizeof(char).
};

struct bloated {
  char   c1;  // Must be at offset 0
  double d;   // Will be at offset alignof(double)
  char   c2;  // Will be at offset (alignof(double) + sizeof(double))
};

答案 2 :(得分:0)

我想我找到了问题的答案。 为什么在char和short之间填充字节可能有两个原因,而不是短暂的。

1)某些体系结构可能有2个字节的指令,只能从内存中获取2个字节。如果是这种情况,则需要2个存储器读取周期来获取短路。

2)某些架构可能没有2字节指令。即使在这种情况下,处理器也会从存储器中取出4个字节到寄存器,并屏蔽不需要的字节以获得短值。如果字节没有在char和short之间填充,则处理器必须移位字节以获得短值。

上述两种情况都可能导致性能下降。这就是为什么字节短是2字节对齐的原因。

答案 3 :(得分:0)

编译器根据目标处理器(micro-)architectureABI的规定对齐数据。以x86-64 ABI spec为例,查找实例。

如果您的编译器与ABI指定的编译器不同,您将无法从库中调用关于该ABI的函数!

在您的示例中,如果(在x86-64上)短字段s未在2个字节上对齐,则处理器必须工作更多(可能发出两次访问)才能获得该字段。

此外,在许多x86-64芯片上,缓存行通常是16个(或更少)字节的倍数。因此,将call stack帧与16个字节对齐是有意义的。对于类似矢量的局部变量(AVX,SSE3等等)

,这是必需的

在某些处理器上,如果数据格式错误,则会导致错误(例如interrupt表示机器异常)或显着降低处理速度。此外,它可以使一些访问非原子(用于多核处理)。因此,一些ABI规定了更多ABI,这是绝对必要的。此外,CPU的一些近期功能(如vectorization,例如SIMDAVXSSE3指令)确实受益于非常对齐的数据(例如,对齐到16个字节)。你的编译器可能会优化更多 - 使用这些指令 - 如果它知道这种强大的对齐。