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

iqa*_*ple 5 c c++ x86 assembly memory-management

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

谢谢

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

Bul*_*ull 5

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


Eri*_*hil 4

有很多不同的处理器模型,我将仅从理论和一般术语来回答这个问题。

\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n