2

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

我对所有 4 个栅栏是如何实现的印象很好,所以我可以解释一下如何保持本地秩序。但是 4 个栅栏只会给出程序顺序;它不会给你 TSO(我知道 TSO 允许旧商店在新负载之前跳转,因此隐式需要 4 个栅栏中的 3 个)。

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

4

1 回答 1

9

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

理论上,大多数保证对于硬件来说是相当容易实现的,只需拥有一个存储缓冲区和一致的共享内存即可。存储缓冲区将 OoO exec 与按顺序提交要求(以及缓存未命中存储)隔离开来,并且可以推测性地执行存储,并且(通过存储->加载转发)重新加载这些存储,而它们仍然是推测性的.

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

    如果使存储对任何其他核心可见的唯一方法使其同时对所有核心可见,则会自动发生这种情况。即通过承诺一致的 L1d。这使得 IRIW 重新排序成为不可能。(MESI 确保一个 store 不能提交到 L1d,除非它由这个 core 独占:没有其他 core 有一个有效的副本。)(一个 core 观察自己的 store 需要一个完整的屏障,否则它将通过 store forwarding 观察自己的 store ,而不是全局总顺序。典型的 IRIW 试金石测试正在考虑总共 4 个线程,因此没有本地重新加载。)

    事实上,很少有硬件没有这个属性。一些POWER CPU可以在同一物理内核上的 SMT 线程之间进行存储转发,这使得 2 个读者可能对 2 个作者的存储顺序产生分歧(IRIW 重新排序)。尽管 x86 CPU 通常也有 SMT(例如 Intel 的超线程),但内存模型要求它们不要在逻辑内核之间进行存储转发。没关系; 无论如何,它们都会对存储缓冲区进行静态分区。 在一个带有 HT 的 Core 上执行的线程之间的数据交换将使用什么?. 还有,生产者-消费者在超同级与非超同级之间共享内存位置的延迟和吞吐量成本是多少?用于实验测试。

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

这些保证的其余部分分别适用于每个(逻辑)CPU 内核。(关于如何在内核之间创建同步的问答。)

  • 存储按程序顺序变为可见:从存储缓冲区按顺序提交到 L1d 缓存。(存储缓冲区条目在发布/重命名期间按程序顺序分配)。这意味着缓存未命中存储必须停止存储缓冲区,而不是让较年轻的存储提交。请参阅为什么退休后的 RFO 不中断内存排序?一个简单的心理模型,以及 Skylake 可能实际做的一些细节(在等待缓存行到达时将来自存储未命中的数据提交到 LFB)。

  • 加载不会在以后的存储中重新排序:简单:需要加载完全完成(已从 L1d 缓存中获取数据)才能退休。由于退休是有序的,并且商店在退休之前无法提交 L1d (变得非投机性),我们可以免费获得 LoadStore 订购1

  • 加载按程序顺序从相干高速缓存(内存)中获取数据。这是一个难点:加载在执行时访问全局状态(缓存),这与存储缓冲区不同的是,存储缓冲区可以吸收 OoO exec 和按顺序提交之间的不匹配。实际上,使每个加载都依赖于先前的加载将防止命中未命中并杀死涉及内存的代码的乱序执行的许多好处。

    在实践中,英特尔 CPU 积极推测,当架构上允许加载发生时(在执行较早的加载之后),现在存在的高速缓存行仍将存在。如果不是这种情况,请核对管道(内存顺序错误推测)。为此有一个性能计数器事件。

在实践中,一切都可能更复杂,以追求更高的性能,或者更多的投机性早期加载。

(在 C ++术语中acq_rel,这至少与核心加载一个从未出现或将出现在内存中的值以供其他线程加载。 全局不可见的加载指令

相关问答:


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

LoadStore 重新排序实际上在有序管道上更容易理解,我们知道需要对缓存未命中负载进行特殊处理才能获得可接受的性能。 如何使用按序提交进行加载->存储重新排序?

于 2020-06-20T01:09:52.107 回答