5

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

谢谢

让我把事情说得更具体一些。编译器使用变量的长度来对齐它。例如,如果一个变量的长度为 256 位,Complier 会将其对齐到 32 字节的边界。我认为没有任何一种 CPU 具有那么长的数据总线。此外,普通的 DDR 内存一次只能传输 64 位数据,尽管有缓存,内存怎么会填满 CPU 更宽的数据总线呢?还是仅通过缓存?

4

2 回答 2

5

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

于 2013-05-23T00:51:13.127 回答
4

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

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

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

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

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

于 2013-05-23T01:08:44.897 回答