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



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

struct s {
 char c;
 short s;
};

在这里,1) 为什么编译器在2字节的边界上短对齐?假设处理器可以在单个内存读取周期内获取4个字节,那么在上述情况下,即使在char和short之间没有填充,读取short不是只需要1个内存读取周期吗?

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

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

因此,如果short有2个字节大,但有4个字节对齐,那么在一个数组中的所有短路之间都会有2字节的漏洞,这是被禁止的。

你确实看到你的假设有点缺陷。我可以制作一个有26个字符的struct,它不会是26字节对齐的。它可以从任何地方开始。具有的N字节类型的对齐方式等于N或除以N

首先,您的前提是不正确的。每个物体都以某种基本的对齐方式对齐。对于某些标量对象,对齐方式可能与对象的数据大小相同,但也可能更小或更大。例如,一个经典的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))
};

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

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

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

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

编译器按照目标处理器(微)体系结构和ABI的规定对齐数据。例如,以x86-64 ABI规范为例。

如果您的编译器与某些ABI指定的对齐方式不同,您将无法从符合该ABI的库中调用函数

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

此外,在许多x86-64芯片上,缓存线通常是16(或更少)字节的倍数。因此,在16字节上对齐调用堆栈帧是有意义的。这对于类似矢量的局部变量(AVX、SSE3等)来说是必要的

在一些处理器上,数据对齐不好会导致故障(例如,机器异常中断)或显著降低处理速度。此外,它可以使一些访问成为非原子访问(用于多核处理)。因此,一些ABI规定了更多的ABI,这是绝对必要的。此外,CPU的一些最新功能(如矢量化,例如通过AVX或SSE3等SIMD指令)确实受益于非常对齐的数据(例如对齐到16字节)。如果你的编译器知道这样的强对齐,那么它可能会优化更多——使用这样的指令。

最新更新