【问题标题】:X86 linearizability?X86 线性化?
【发布时间】:2020-08-27 19:30:30
【问题描述】:

X86 不提供开箱即用的顺序一致性 (SC)。

X86 提供 TSO;因此它将免费提供以下障碍

[LoadLoad]
[LoadStore]
[StoreStore]

常规加载提供获取语义。

r1=A
[LoadLoad]
[LoadStore]
...

常规商店提供发布语义。

...
[StoreStore]
[LoadStore]
X=r1

因此,用于常规加载和存储的 X86 提供了获取/释放语义。

这对于 SC 来说是不够的,例如

[StoreStore]
[LoadStore]
X=r1
r2=Y
[LoadStore]
[LoadLoad]

在这种情况下,存储和加载仍然可以重新排序,因此它不是 SC。为了解决这个问题,可以添加一个 [StoreLoad] 屏障(例如 MFENCE)。

[StoreStore]
[LoadStore]
X=r1
[StoreLoad]<--
r2=Y
[LoadStore]
[LoadLoad]

所以现在我们已经从获取/释放语义升级到 SC。

在大多数情况下,读取比写入更频繁,因此对写入执行 [StoreLoad] 是最有益的。

[StoreStore]
[LoadStore]
X=r1
[StoreLoad]

我的问题是关于线性化的。线性化和 SC 之间的区别在于,对于 SC,操作的效果可以在调用开始之前或调用完成之后倾斜,但是对于线性化,要求调用的效果在调用开始和调用完成之间。

这让我产生疑问; X86 可以提供线性化吗?

让我们首先确定调用的开始和完成:

Invocation start:指令的发出;因此,当 ROB 上的条目被保留时。

调用完成:删除 ROB 的指令(例如,当项目从 SB 移动到 L1D 时的存储情况下)。

当从缓存或内存中读取数据时,负载将成为全局可见的。这是在开始之后和完成之前。 MESI 协议将防止负载读取过时的值。

当商店离开 SB 并到达 L1d 时,商店将变得全局可见。这也在调用开始和完成之间。

所以在我看来 X86 可以提供线性化。

【问题讨论】:

  • 我上面对完成的定义被打破了。从 ROB 中删除存储并不意味着它是全局可见的。为此,它需要离开 SB。

标签: x86 memory-barriers consistency


【解决方案1】:

商店在从 ROB 退休时不会承诺 L1d。这将不必要地将执行绑定到提交,从而失去隐藏偶尔缓存未命中存储的一些好处。 (这个好处甚至适用于有序 CPU。)

当存储从 ROB 中退出时,存储缓冲区条目“毕业”并成为提交到 L1d 的候选对象。提交不能在退休之前发生。它发生在一段时间后,一旦它到达 SB 队列的头部(在 x86 上,提交按程序顺序)。提交到 L1d 是它成为全球可见的时刻。

(存储缓冲区总是尽可能快地将自身排入 ROB。mfencelocked 指令只会让这个核心等待在执行后续加载之前发生这种情况。 )

如果我正确理解了您对“可线性化”的定义,那么除了 内存 障碍之外,您还需要额外的障碍来提供它。


lfence 在乱序后端序列化执行(在后续指令发出之前耗尽 ROB),所以mfence + lfence 我可以认为完全序列化执行 +通过在要完全分开的两条指令之间设置这样的屏障来提交内存。 (例如,在存储之后,rdtsc 之前将记录存储缓冲区何时耗尽。)

或使用序列化指令,如cpuid。英特尔在其手册中使用的技术术语是“序列化指令”,即在先前的指令退役之前无法启动的指令,并在后续指令发出之前耗尽存储缓冲区。这就是我认为你所说的“线性化”。 MFENCE/SFENCE/etc "serialize memory but not instruction execution"?

How many memory barriers instructions does an x86 CPU have? 列出了 x86 的序列化指令。


或者如果您将“调用完成”定义为“提交到 L1d”,那么线性化与 x86 和几乎所有 ISA 上的 SC 相同:一旦将存储提交到 L1d 缓存,它就是对所有核心全局可见。并且根据定义,在发生这种情况之前,核心几乎不会跟踪自己的存储。

我们运行线程的所有 CPU 都具有缓存一致的共享内存,因此不需要显式刷新来确保可见性,并且保持一致 L1d = 全局可见。 MESI 一致性要求高速缓存行在修改之前由核心独占拥有。

【讨论】:

  • 感谢您的回复。如果使用r1=X,[LoadLoad][LoadStore]... 实现加载并使用...[StoreStore][LoadStore],Y=r2,[StoreLoad].... 实现存储,这在X86 上是否被认为是可线性化的?它是 SC.. 但 X86 是否保证指令的效果在调用开始/完成范围内?
  • @pveentjer:这取决于您如何定义“调用完成”。您给出了两个相互矛盾的从 ROB 退休与从 SB 提交到 L1d 的定义。直到现在我才注意到,更新了我的答案。
  • 感谢您的回答。我知道我的定义被打破了,我缺乏上周获得的重要见解。如果完成被定义为存储离开 SB 并且适当的障碍存在,X86 可以提供线性化;不仅仅是顺序一致性。只要不违反程序顺序,就可以使用顺序一致性加载和存储。但是对于线性化,这是不允许的。
  • 关于序列化指令的问题。它阻止前端发出指令。但是已经发布的较新的负载会发生什么?使用 MFENCE,这些会被阻塞,直到 SB 被耗尽。但是它们会被序列化指令阻塞吗?那么序列化指令会停止前端发出指令并停止加载执行,还是只会停止前端发出指令但已经发出加载继续?
  • 后端不能有任何比序列化指令更年轻的指令(包括加载)。关键是它(至少在逻辑上)耗尽所有内容,然后完全执行 + 自行退出,然后允许前端发出以后的指令。即前端停止/暂停序列化指令,就像它对 LFENCE 所做的那样。
猜你喜欢
  • 1970-01-01
  • 2018-12-09
  • 2011-07-16
  • 2015-08-23
  • 1970-01-01
  • 2013-04-20
  • 2021-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多