GNU文档中指出,在64位系统上,malloc
按16字节的倍数对齐。这是为什么呢?
如果我理解正确,寄存器和所有指令操作的值最多只有8个字节宽度。因此,看起来8字节对齐应该是必须的。
注:
- 我发现栈指针必须是16字节对齐的一些理由,但其他内存地址没有要求
- 我看到一些评论说这只是为了减少内部碎片,并不太合理,因为文档中明确指出他们特别为
x86_64
移动到了16字节对齐。
GNU文档中指出,在64位系统上,malloc
按16字节的倍数对齐。这是为什么呢?
如果我理解正确,寄存器和所有指令操作的值最多只有8个字节宽度。因此,看起来8字节对齐应该是必须的。
注:
x86_64
移动到了16字节对齐。x86_64使用xmm寄存器(大量使用 -- 所有的浮点运算都在xmm寄存器中进行,因为8087浮点寄存器已被弃用),并且xmm寄存器要求16字节对齐以实现(高效)访问。
因此,x86_64中的大多数东西(包括由malloc分配的堆栈和堆内存)都是按照16字节对齐来组织的,这样编译器就可以随时使用“对齐”指令来处理xmm寄存器而不需要使用(可能更慢的)非对齐指令。
在较新的硬件上,当内存对齐时,编译器甚至不需要费劲地使用对齐指令 -- 非对齐指令与对齐指令一样快。
long double
类型,并将其填充到16字节,使得 alignof(long double) == 16
,从而确保long double永远不会跨越缓存行边界。(这是否值得,我不知道;可能SSE2是支持16字节对齐的动机之一)。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空间的循环时,它就知道它将是对齐的。malloc
可以为小型分配(例如1到15个字节)返回不太对齐的空间,因为该空间仍然可以用于容纳适合的任何类型。(在C中,对象不能要求比其大小更多的对齐方式。例如,您不能定义一个始终需要位于缓存行开头的int
,或者如果您这样做,则sizeof将扩展带填充。)
long double
可能更长,还有SSE类型,尽管它们是语言的扩展)。 - Some programmer dudemalloc
的库与你的程序在不同的时间编译,每个编译单元都是分别编译的。编译器对其他 C 文件或库中的代码一无所知。 - Gerhardh