成功的未对齐访问对x86的实际影响是什么?

sha*_*oth 17 memory performance x86 alignment memory-alignment

我总是听说未对齐的访问很糟糕,因为它们会导致运行时错误并导致程序崩溃或减慢内存访问速度.但是我找不到任何关于它们会减慢速度的实际数据.

假设我在x86上并且有一些(但未知)未对齐访问的共享 - 实际可能的最差减速是什么?如何在不消除所有未对齐访问和比较两个版本代码的运行时间的情况下估算它?

Nec*_*lis 17

这取决于指令,对于大多数x86 SSE加载/存储指令(不包括未对齐的变体),它将导致错误,这意味着它可能会导致程序崩溃或导致大量往返异常处理程序(这意味着几乎或所有表现都会丢失).未对齐的加载/存储变体的运行量是周期IIRC的两倍,因为它们执行部分读/写操作,因此执行操作需要2个(除非你很幸运,它在缓存中,这大大减少了惩罚).

对于一般的x86加载/存储指令,惩罚是速度,因为执行读取或写入需要更多周期.未对齐也可能影响缓存,导致缓存行拆分和缓存边界跨越.它还可以防止读取和写入时的原子性(对于x86的所有对齐读/写保证,阻塞和传播是另外的东西,但是对未对齐数据使用LOCK'指令可能会导致异常或大大增加已经大量的惩罚lock incurs),这是并发编程的禁忌.

英特尔x86和x64优化手册详细介绍了上述每个问题,它们的副作用以及如何解决它们.

Agner Fog的优化手册应该具有您在原始循环吞吐量方面所需的确切数字.

  • @NitsanWakart:英特尔架构和指令集手册中的 4.1.1 指出,任何未对齐的访问都需要 2 次加载/存储,这基本上会产生两倍的周期(但这可能会因其他条件而异):`交叉的字或双字操作数4 字节边界或跨越 8 字节边界的四字操作数被认为是未对齐的,需要两个单独的内存总线周期才能访问。` (3认同)

Ana*_*ile 6

一般来说,估计现代处理器的速度非常复杂。这不仅适用于未对齐的访问,而且适用于一般情况。

现代处理器具有流水线架构,指令的乱序和可能并行执行以及可能影响执行的许多其他事物。

如果不支持未对齐访问,则会出现异常。但是,如果它得到支持,您可能会或可能不会减速,这取决于很多因素。这些因素包括您在未对齐指令之前和之后执行的其他指令(因为处理器可能能够在执行先前指令时开始获取数据,或者在等待时继续执行后续指令)。

如果跨缓存行边界发生未对齐访问,则会发生另一个非常重要的区别。一般来说,对于未对齐的访问可能会发生对缓存的 2 倍访问,真正的减速是如果访问跨越缓存线边界并导致双缓存未命中。在最坏的情况下,2 字节未对齐读取可能需要处理器将两个缓存行刷新到内存中,然后从内存中读取 2 个缓存行。这是大量的数据移动。

优化的一般规则也适用于此:首先编码,然后测量,然后当且仅当存在问题时找出解决方案。


har*_*old 6

在某些英特尔微架构中,由高速缓存行边界分割的负载比平时延长十几个周期,并且由页面边界分割的负载需要超过200个周期.如果负载在循环中始终未对齐,那就足够了,即使palignr不是一个选项,也值得做两个对齐的加载并手动合并结果.即使是SSE的未对齐载荷也不会拯救你,除非它们正好在中间分开.

在AMD上,这从来都不是问题,而问题大多在Nehalem中消失了,但仍然有很多Core2也存在.