为什么malloc需要16字节对齐?

3

2
也许是因为栈需要16字节对齐的原因?请注意,C语言中的某些类型可能大于8个字节(例如long double可能更长,还有SSE类型,尽管它们是语言的扩展)。 - Some programmer dude
有趣。如果那是原因的话,他们似乎可以在编译时将其轻松地对齐到最大类型。 对于SSE类型的对齐似乎是我遇到的最有说服力的原因。 但是,没有人明确表示这一点。 - Moss Richardson
你所说的“在编译时最大类型”是什么意思?你是建议编译器检查程序使用的最大类型并据此进行调整吗?包含 malloc 的库与你的程序在不同的时间编译,每个编译单元都是分别编译的。编译器对其他 C 文件或库中的代码一无所知。 - Gerhardh
在glibc的编译时,编译器显然知道目标架构和字长。某些类型保证默认为字长(至少在使用gcc时是这样)。当编译malloc时,它们可以检测字长并相应地设置它。显然,他们选择了比字长更大的东西,这就是我的观点。 - Moss Richardson
2个回答

7

x86_64使用xmm寄存器(大量使用 -- 所有的浮点运算都在xmm寄存器中进行,因为8087浮点寄存器已被弃用),并且xmm寄存器要求16字节对齐以实现(高效)访问。

因此,x86_64中的大多数东西(包括由malloc分配的堆栈和堆内存)都是按照16字节对齐来组织的,这样编译器就可以随时使用“对齐”指令来处理xmm寄存器而不需要使用(可能更慢的)非对齐指令。

在较新的硬件上,当内存对齐时,编译器甚至不需要费劲地使用对齐指令 -- 非对齐指令与对齐指令一样快。


正常的浮点运算不会在malloc分配的内存中进行16字节的加载/存储。正常的FP操作可能会触发16字节的溢出和重新加载,但这是针对堆栈内存的。唯一可以得到一个16字节的malloc内存存储方式就是自己编写代码实现,并且您也可以在32位代码中实现。 - harold
@harold:如果你在大多数编译器上启用了优化(至少是gcc和clang),它们会将访问所有类型的内存(不仅仅是堆栈)转换为更宽的访问。不幸的是,在gcc上,您仍然需要明确告诉它东西对齐以使用对齐访问——它不能从malloc的使用中自动推断出来。但在较新的硬件上,这似乎并不太重要——当内存对齐时,未对齐的操作与对齐的操作一样快。 - Chris Dodd

3
x86-64 System V使用x87处理80位long double类型,并将其填充到16字节,使得 alignof(long double) == 16,从而确保long double永远不会跨越缓存行边界。(这是否值得,我不知道;可能SSE2是支持16字节对齐的动机之一)。
但是,SSE技术并不是唯一促进 alignof(max_align_t) == 16(设置malloc允许返回的最小对齐方式)的因素之一。
例如,存在的__m128i并没有直接对max_align_t产生任何影响。32位C实现支持它,但具有较低的malloc保证。当AVX系统支持时,__m256i的存在并未增加分配器的对齐保证。(如何解决AVX加载/存储操作的32字节对齐问题)。但显然,向量化(自动和手动)更方便,如果malloc的内存对于 movaps足够对齐,特别是在旧CPU上,当x86-64是新的,即使内存对齐时,movups也会有惩罚。如果编译器只看到float*,很难利用这个保证。您可能已经将指针传递到分配的内存中。但是,如果它能够看到一个输出数组的malloc,那么在自动向量化写入这个新malloc空间的循环时,它就知道它将是对齐的。
顺便说一下,在ISO C中,malloc可以为小型分配(例如1到15个字节)返回不太对齐的空间,因为该空间仍然可以用于容纳适合的任何类型。(在C中,对象不能要求比其大小更多的对齐方式。例如,您不能定义一个始终需要位于缓存行开头的int,或者如果您这样做,则sizeof将扩展带填充。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接