本文介绍了现代英特尔x86 CPU如何实现商店的总订单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于其TSO内存模型,x86保证了所有存储的总顺序。我的问题是,有没有人知道这实际上是如何实施的。

我对所有4个围栏是如何实施的印象很好,所以我可以解释当地的秩序是如何保持的。但4个栅栏只会给出程序顺序;它不会给您提供TSO(我知道TSO允许较旧的商店跳到新负载之前,因此只需要4个栅栏中的3个)。

在单个地址上的所有内存操作的总顺序是一致性的责任。但我想知道英特尔(尤其是Skylake)如何在多个地址的商店上实现总订单。

推荐答案

x86TSO内存模型基本上相当于程序顺序加上一个具有存储转发功能的存储缓冲区。(486硬件就是这么简单;后来的CPU没有引入新的重新排序。)

大多数由此产生的保证在理论上对于硬件来说都很容易实现,只需拥有存储缓冲区和一致的共享内存;存储缓冲区将OOO EXEC与顺序提交要求(和缓存未命中存储)隔离开来,并在这些存储仍然是推测性的情况下重新加载它们(通过存储加载转发)。

  • 所有核心可以就所有存储发生的总顺序达成一致。或者更准确地说,核心不能在它们实际观察到的总顺序的任何部分不一致。存储到2个不同行可以真正同时进行,因此任何观测都与假设的总顺序中的任何一个顺序兼容。

    如果使存储对任何其他内核可见的唯一方法是使其同时对所有内核可见,则会自动发生这种情况。即通过致力于一致的L1d。这使得IRIW无法重新排序。(MESI确保存储不能使用L1d,除非它由该核心独占:没有其他核心拥有有效的副本。)(一个观察自己门店的核心需要一个完整的屏障,否则它将通过门店转发来观察自己的门店,而不是全球总订单。典型的IRIW Litmus测试考虑总共4个线程,因此没有本地重新加载。)

    事实上,任何硬件都很少有这个属性;有些POWER CPUs can store-forward between SMT threads on the same physical core,这使得2个读取器可能会对2个写入器的存储顺序(IRIW重新排序)产生分歧。尽管x86 CPU通常也有SMT(例如Intel的超线程),但内存模型要求它们不能在逻辑核心之间进行存储转发。这很好;他们无论如何都会静态地对存储缓冲区进行分区。What will be used for data exchange between threads are executing on one Core with HT?。以及What are the latency and throughput costs of producer-consumer sharing of a memory location between hyper-siblings versus non-hyper siblings?用于实验测试。

唯一发生的重新排序是在每个CPU核心内,在其对全局一致共享状态的访问之间进行的局部重新排序。(这就是为什么本地内存屏障只会使此核心等待发生事情,例如等待存储缓冲区排空,可以在x86 TSO之上恢复顺序一致性。这同样适用于较弱的内存模型,BTW:只是在MESI一致性之上进行局部重新排序。)

其余的保证分别适用于每个(逻辑)CPU核心。(Q&A关于这如何在核心之间创建同步。)

  • 存储按程序顺序可见:按顺序从存储缓冲区提交到L1d缓存。(在发布/重命名期间,按程序顺序分配存储缓冲区条目)。这意味着缓存未命中存储必须停止存储缓冲区,不让较年轻的存储提交。有关这一点的简单心理模型,请参阅Why doesn't RFO after retirement break memory ordering?,以及Skylake可能实际执行的操作的一些详细信息(在等待缓存线到达时将存储未命中的数据提交到LFBs)。

  • 加载不会与以后的存储一起重新排序:简单:要求加载完全完成(已从L1d缓存获取数据),然后才能停用。由于已按顺序停用,并且存储在停用(变为非投机性)后才能提交到L1d,因此我们获得免费的LoadStore订购。

  • 加载按程序顺序从一致缓存(内存)获取数据。这是一个困难的问题:执行时加载访问全局状态(缓存),这与存储缓冲区可以吸收OOO EXEC和按顺序提交之间的不匹配不同。实际上,使每个加载依赖于以前的加载将防止命中失误,并扼杀了对涉及内存的代码进行无序执行的许多好处。

    在实践中,英特尔CPU积极推测,当体系结构允许发生加载时(在较早的加载执行之后),现在存在的缓存线将仍然存在。如果不是这样,那就停止流水线(内存顺序错误推测)。有一个与此相关的性能计数器事件。

实际上,要追求更高的性能,或者对于投机性的早期负载,一切都可能变得更加复杂。

(在C++术语中,这至少与acq_rel一样强,但也涵盖了在C++中可能是UB的事物的行为。例如,将最近存储的部分重叠到另一个线程也可能正在读取或写入的位置,从而允许此核心加载一个从未出现在内存中或将出现在内存中的值,以供其他线程加载。Globally Invisible load instructions)

相关问答:


脚注1:
一些OOO EXEC排序较弱的CPU可以执行LoadStorere排序,大概是通过让加载从ROB中退出,只要加载检查了权限并请求了缓存线(对于未命中),即使数据实际上还没有到达。需要对寄存器未准备就绪进行一些单独跟踪,而不是通常的指令调度程序。

在按顺序管道上,LoadStore重新排序实际上更容易理解,我们知道需要对缓存未命中加载进行特殊处理才能获得可接受的性能。How is load->store reordering possible with in-order commit?

这篇关于现代英特尔x86 CPU如何实现商店的总订单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 01:43