我总是听说未对齐的访问很糟糕,因为它们会导致运行时错误并导致程序崩溃或减慢内存访问速度。但是,我无法找到关于它们会减慢速度的实际数据。
假设我在x86上并且有一些(但未知)未对齐访问的份额 - 实际可行的最差减速是什么?如何在不消除所有未对齐访问和比较两个运行时间的情况下估算它代码版本?
答案 0 :(得分:15)
这取决于指令,对于大多数x86 SSE加载/存储指令(不包括未对齐的变体),它会导致错误,这意味着它可能会导致程序崩溃或导致大量往返异常处理程序(这意味着几乎或所有性能都会丢失)。未对齐的加载/存储变体的运行量是周期IIRC的两倍,因为它们执行部分读/写操作,因此执行操作需要2个(除非你很幸运,它在缓存中,这大大减少了惩罚)。
对于一般x86加载/存储指令,惩罚是速度,因为执行读取或写入需要更多周期。未对齐也可能影响缓存,导致缓存行拆分和缓存边界跨越。它还可以防止读取和写入时的原子性(对于x86的所有对齐读/写保证,阻塞和传播是另外的东西,但是对未对齐数据使用LOCK'指令可能会导致异常或大大增加已经大量的惩罚lock incurs),这是并发编程的禁忌。
Intels x86 & x64 optimizations manual详细介绍了上述每个问题及其副作用以及如何纠正这些问题。
Agner Fog' optimization manuals应该具有您在原始周期吞吐量方面所需的确切数字。
答案 1 :(得分:6)
在某些英特尔微架构中,由高速缓存行边界分割的负载比平时延长十几个周期,并且按页面边界分割的负载需要超过200个周期。如果负载在循环中始终未对齐,那么就足够了,即使palignr
不是一个选项,也值得做两个对齐的加载并手动合并结果。即使是SSE的未对齐载荷也不会拯救你,除非它们正好在中间分开。
在AMD上,这从来都不是问题,问题大多在Nehalem中消失了,但是Core2还有很多。
答案 2 :(得分:3)
一般来说,估算现代处理器的速度非常复杂。这不仅适用于未对齐的访问,而且通常也是如此。
现代处理器具有流水线架构,无序以及可能并行执行指令以及可能影响执行的许多其他因素。
如果不支持未对齐访问,则会出现异常。但如果它得到支持,你可能会或可能不会因为很多因素而减速。这些因素包括您在未对齐的指令之前和之后执行的其他指令(因为处理器可能能够在执行先前的指令时开始获取数据,或者在等待时继续执行后续指令)。
如果未对齐的访问跨越高速缓存行边界发生,则会发生另一个非常重要的区别。通常,对于未对齐访问,可能会发生对缓存的2x访问,如果访问跨越缓存行边界并导致双缓存未命中,则实际减速。在最糟糕的情况下,2字节未对齐读取可能需要处理器将两个高速缓存行清除到内存中,然后从内存中读取2个字符串。这是一大堆数据移动。
优化的一般规则也适用于此:第一个代码,然后是度量,然后当且仅当有问题找出解决方案。