为什么128位变量应该对齐到16字节边界

5
正如我们所知,X86 CPU拥有64位数据总线。我的理解是CPU无法访问任意地址。CPU可以访问的地址是其数据总线宽度的整数倍。为了提高性能,变量应该从(对齐到)这些地址开始,以避免额外的内存访问。32位变量对齐到4字节边界将自动对齐到8字节(64位)边界,这对应于x86 64位数据总线。但为什么编译器将128位变量对齐到16字节边界?而不是8字节边界?
谢谢
让我更具体地说明一下。编译器使用变量的长度来对齐它。例如,如果一个变量长度为256位,编译器将把它对齐到32字节边界。我不认为有任何一种CPU有那么长的数据总线。此外,普通DDR内存每次只传输64位数据,尽管有缓存,但是内存如何填满CPU更宽的数据总线?或者只能通过缓存实现吗?

3
众所周知,X86 CPU拥有64位数据总线——这是不正确的。X86并没有规定数据总线大小。现代处理器的数据总线宽度实际上比这更大。 - Mysticial
4
处理器不从数据总线读取数据,而是从高速缓存中读取。为避免跨越高速缓存行边界,需要进行16字节对齐。 - Hans Passant
3
不。Intel Core 2、Nehalem和Sandy Bridge处理器具有128位宽的载入/存储端口。不确定AMD的情况,但我认为它们自K8以来也拥有128位宽的载入/存储端口。缓存层级之间和到内存之间的数据总线甚至更大。(考虑缓存行大小) - Mysticial
1
32位变量在4字节边界对齐,不需要在任何CPU上将它们对齐到8字节边界。 - markgz
@iqapple "数据总线是CPU获取数据的唯一手段" - 是的,但如果它们在途中被缓存,那么数据总线大小将不会影响变量对齐。 - j_kubik
显示剩余8条评论
2个回答

5

一个原因是,大多数X86上的SSE2指令需要数据128位对齐。这个设计决策是基于性能考虑以及避免过于复杂(因此速度缓慢且占用空间大)的硬件。


我认为这可能是正确的。我陷入了一个循环中,试图弄清楚哪些编译器自动对用于矢量化SIMD计算的__m128i类型进行对齐。 - Erik Garrison

4
有很多不同的处理器型号,我将仅在理论和一般性方面回答这个问题。考虑一个16字节对象数组,它从一个地址开始,该地址是8字节的倍数但不是16字节。假设处理器有一个8字节总线,如问题中所示,即使一些处理器没有。然而,请注意,在数组的某个点上,其中一个对象必须跨越页面边界:内存映射通常在以4096字节边界开始的4096字节页面中工作。对于一个8字节对齐的数组,数组的某个元素将从一个页面的4088字节开始,并延续到下一个页面的第7字节。
当程序尝试加载跨越页面边界的16字节对象时,它不能再执行单个虚拟到物理内存的映射。它必须为前8个字节进行一次查找,为后8个字节进行另一次查找。如果装载/存储单元未设计用于此,则指令需要特殊处理。处理器可能会中止其最初尝试执行指令的尝试,将其分成两个特殊的微指令,并将其发送回指令队列以供执行。这可能会延迟指令多个处理器周期。
此外,如Hans Passant所指出的,对齐与缓存交互。每个处理器都有一个内存缓存,通常将缓存组织成32字节或64字节的“行”。如果您加载一个16字节对齐的对象,并且该对象在缓存中,则缓存可以提供包含所需数据的一个缓存行。如果您从未对齐的数组中加载16字节对象,则数组中的某些对象将跨越两个缓存行。当这些对象被加载时,必须从缓存中获取两个行。这可能需要更长时间。即使获取两个行不需要更长时间,也可能会干扰程序正在执行的其他操作。通常,程序将从多个位置加载数据。如果加载有效,则处理器可以同时执行两个操作。但是,如果其中一个需要两个缓存行而不是正常的一个,则它会阻止其他加载操作的同时执行。
此外,一些指令明确要求对齐地址。处理器可能更直接地分派这些指令,绕过一些修复没有对齐地址的操作的测试。当解析这些指令的地址并发现它们未对齐时,处理器必须中止它们,因为已绕过修复操作。

我知道你是对的,即使有些观点对我来说很深奥。 - iqapple
在我看来,大部分回答都是正确的,但与问题本身无关。问题是“为什么编译器将128位变量对齐到16字节边界?”,简单地回答这个问题就是硬件需要这样做,编译器之所以这样做并不是因为更高效,而是因为其他方式行不通。当你说“考虑一个以八字节为倍数开始但不是16字节的地址的16字节对象数组。”时,这只是行不通的(因为CPU硬件不支持它),无论数组是否跨越页面边界。 - Bull
实际上这取决于问题对“变量”的理解。我想到的是像__m128i这样的128个变量。如果是关于诸如struct foo {char x[128];};之类的东西,那么我同意Eric的观点。 - Bull
Eric,你夸大了数据对齐的重要性。一些人通过现代x86处理器(即Core i7)上的实验证据支持这一观点:读取或写入未对齐的内存操作数不会有性能惩罚。更公正的说法是,在极端情况下,非对齐数据确实会造成很大影响,但通常在x86上并不重要。至于128位数据,这也是问题的主题,说SIMD指令可以处理对齐数据,因为其中一个指令可以将数据复制到对齐位置,这并不能改变SSE2硬件需要数据对齐的事实。 - Bull
@user2151446:我的回答描述了一些对齐方式如何影响性能的机制。除了提到这些机制可能在某些情况下导致一些延迟周期之外,它并没有评估这些机制的重要性。如果您能指出过于强调且不正确的具体句子,我会进行更正。如果您无法指出这样的句子,请将您的反对票改为赞成票。 - Eric Postpischil
显示剩余3条评论

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