我了解到一些CPU会重新排序指令,但对于单线程程序来说这并不是问题(指令仍将被重新排序,但看起来就像指令是按顺序执行的),只有对于多线程程序才是问题。
为了解决指令重新排序的问题,我们可以在代码中适当的位置插入内存屏障。
但x86 CPU是否会重新排序指令呢?如果它不会重新排序,则无需使用内存屏障,对吗?
我了解到一些CPU会重新排序指令,但对于单线程程序来说这并不是问题(指令仍将被重新排序,但看起来就像指令是按顺序执行的),只有对于多线程程序才是问题。
为了解决指令重新排序的问题,我们可以在代码中适当的位置插入内存屏障。
但x86 CPU是否会重新排序指令呢?如果它不会重新排序,则无需使用内存屏障,对吗?
是的,来自英特尔和AMD的所有现代x86芯片都会在一个窗口中积极地重新排序指令,该窗口在两个制造商最近的CPU上大约有200条指令深度(即新指令可能执行,而旧指令“过去”超过200条指令仍在等待)。这通常对单个线程不可见,因为CPU仍通过尊重依赖关系维护当前线程的串行执行幻象1,所以从当前执行线程的角度来看,这些指令的执行方式就好像它们是按顺序执行的。
那应该回答了标题问题,但您的第二个问题是关于内存屏障的。但是,它包含一个错误的假设,即指令重新排序必然导致(并且是唯一的)可见内存重新排序。实际上,指令重新排序既不充分也不必要进行跨线程内存重新排序。
现在确实可以肯定的是,乱序执行是乱序访问内存能力的主要驱动程序,或者说是追求MLP(内存级并行性)的能力,推动现代CPU不断增强其乱序能力。实际上,两者可能同时成立:不断增强的乱序能力在很大程度上从强大的内存重新排序功能中受益,而同时也没有好的乱序能力,就无法进行激进的内存重新排序和重叠,因此它们在一种自我加强的超过部分的循环中相互帮助。
因此,乱序执行和内存重新排序肯定存在关系;但是,您可以轻松地获得重新排序而没有乱序执行!例如,核心本地存储缓冲区通常会导致表面上的重新排序:在执行点时,存储不会直接写入缓存(因此在一致性点上不可见),这会延迟本地存储与需要在执行点读取其值的本地加载之间的时间。
正如Peter在评论线程中指出的那样,当允许按顺序设计中的负载重叠时,还可以获得一种负载-负载重新排序类型:负载1可能开始,但在缺少使用其结果的指令的情况下,流水线按顺序设计可能继续执行接下来的指令,其中可能包括另一个负载2。如果负载2是缓存命中,而负载1是缓存未命中,则负载2可能从负载1更早地得到满足,因此表面上的顺序可能会被交换重新排序。
因此,我们看到,并非所有的跨线程内存重新排序都是由指令重新排序引起的,但某些指令重新排序也意味着乱序访问内存,对吧?不要那么快!这里有两种不同的上下文:发生在硬件级别例如,在x86中,现代芯片会自由地重排序几乎任何负载和存储流:如果一个加载或存储准备好执行,CPU通常会尝试执行它,即使存在尚未完成的较早的加载和存储操作。
mfence
)的使用仅限于您没有执行原子读取修改写入操作的情况。mfence
可以防止它。
mfence
。(但所有现代的x86 CPU都具有完全的乱序执行能力。) - Peter Cordes