26

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

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

4

3 回答 3

23

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

对于一般的 x86 加载/存储指令,代价是速度,因为读取或写入需要更多的周期。不对齐也可能影响缓存,导致缓存行拆分和缓存边界跨越。它还可以防止读取和写入的原子性(保证所有对齐的 x86 读取/写入,障碍和传播是另一回事,但是在未对齐的数据上使用 LOCK 指令可能会导致异常或大大增加本已巨大的惩罚lock incurs),这是并发编程的禁忌。

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

Agner Fog 的优化手册应该包含您正在寻找的原始循环吞吐量的确切数字。

于 2012-09-19T09:14:19.317 回答
7

在某些 Intel 微架构上,由高速缓存线边界分割的负载比平时多花费十几个周期,而由页面边界分割的负载则多花费 200 多个周期。糟糕的是,如果负载在循环中始终未对齐,则值得进行两次对齐的负载并手动合并结果,即使palignr不是一个选项。即使是 SSE 的未对齐负载也无法拯救您,除非它们完全从中间分开。

在 AMD 上,这从来都不是问题,在 Nehalem 中问题基本消失了,但仍然有很多 Core2 存在。

于 2012-09-19T10:31:46.713 回答
6

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

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

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

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

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

于 2012-09-19T09:27:30.703 回答