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

时间:2013-05-22 23:27:57

标签: c++ c memory-management assembly x86

众所周知,X86 CPU有64位数据总线。我的理解是CPU无法访问任意地址。 CPU可以访问的地址是其数据总线宽度的整数倍。对于性能,变量应从这些地址开始(对齐)以避免额外的内存访问。与4Byte边界对齐的32位变量将自动对齐到8Byte(64位)边界,这对应于x86 64位数据总线。但是为什么编译器将128位变量与16Byte边界对齐?不是8Byte边界?

由于

让我更具体一点。编译器使用变量的长度来对齐它。例如,如果变量的长度为256位,则Complier会将其与32Byte边界对齐。我不认为有任何类型的CPU具有那么长的数据总线。此外,常见的DDR存储器一次只传输64位数据,尽管有缓存,但内存如何填满CPU更宽的数据总线?或仅通过缓存?

2 个答案:

答案 0 :(得分:5)

一个原因是X86上的大多数SSE2指令要求数据为128位对齐。这个设计决策是出于性能原因而做出的,并且避免了过于复杂(因而又慢而大)的硬件。

答案 1 :(得分:4)

有许多不同的处理器型号,我将仅在理论和一般术语中回答这个问题。

考虑一个16字节对象的数组,它从一个8字节的倍数但不是16字节的地址开始。假设处理器有8字节总线,如问题所示,即使某些处理器没有。但请注意,在数组中的某个点,其中一个对象必须跨越页边界:内存映射通常在4096字节页面中起作用,这些页面以4096字节边界开始。使用8字节对齐的数组,数组的某些元素将从一页的字节4088开始,并继续到下一页的第7字节。

当程序尝试加载跨越页边界的16字节对象时,它不再能够执行单个虚拟到物理内存映射。它必须对前八个字节进行一次查找,对另外八个字节进行另一次查找。如果加载/存储单元不是为此设计的,那么该指令需要特殊处理。处理器可能会中止其执行指令的初始尝试,将其划分为两个特殊的微指令,并将它们发送回指令队列以供执行。这可以将指令延迟许多处理器周期。

此外,正如Hans Passant所指出的,对齐与缓存交互。每个处理器都有一个内存缓存,缓存通常被组织成32字节或64字节的“行”。如果加载一个16字节对齐的16字节对象,并且该对象位于缓存中,则缓存可以提供一个包含所需数据的缓存行。如果从非16字节对齐的数组加载16字节对象,则阵列中的某些对象将跨越两个缓存行。加载这些对象时,必须从缓存中提取两行。这可能需要更长时间。即使不需要更长的时间来获得两条线,也许是因为处理器被设计为每个周期提供两个缓存线,这可能会干扰程序正在执行的其他操作。通常,程序将从多个位置加载数据。如果负载有效,则处理器可以一次执行两次。但是,如果其中一个需要两个缓存行而不是正常缓存行,那么它会阻止同时执行其他加载操作。

此外,某些指令明确要求对齐的地址。处理器可以更直接地发送这些指令,绕过一些没有对齐地址来修复操作的测试。当解决这些指令的地址并发现它们未对齐时,处理器必须中止它们,因为已经绕过了修复操作。