MESI在Intel 64和IA-32上有什么意义

时间:2018-04-15 15:37:15

标签: caching concurrency x86 cpu-architecture mesi

  • MESI的目的是保留共享内存系统的概念。
  • 但是,对于商店缓冲区,事情很复杂:
  • 一旦数据到达MESI实施缓存,内存就会连贯下游。
  • 然而,在上游,每个核心可能不同意内存位置X中的内容,这取决于每个核心的本地存储缓冲区中的内容。
  • 因此,从每个核心的角度来看,似乎记忆的状态是不同的 - 它不是连贯的。
  • 那么,为什么我们要打扰"部分"加强与MESI的一致性?

编辑:在进一步缩小我真正困惑的内容后,进行了大量修改。我试图保持问题的一般概念相同,以保持所收到的重要答案的相关性。

2 个答案:

答案 0 :(得分:4)

x86上的MESI与几乎任何多核/ CPU系统相同:强制执行缓存一致性。在x86上,没有“部分一致性”用于等式的高速缓存一致性部分:高速缓存完全相干。因此,可能的重新排序是连贯的缓存系统和与核心本地组件(如加载/存储子系统(尤其是存储缓冲区)和其他无序机制)的交互的结果。

该交互的结果是x86提供的架构强内存模型,只有有限的重新排序。没有连贯的缓存,你根本无法合理地实现这个模型,或几乎任何除了完全弱 1 以外的模型。

你的问题似乎嵌入了这样的假设:只有可能的状态是“连贯的”和“其他所有的东西”。此外,还有一些混合了缓存一致性(主要是处理缓存,主要是隐藏的细节)和内存一致性模型的想法。在架构上定义并将由每个架构 2 实现。维基百科explains,缓存一致性和内存一致性之间的一个区别是,前者的规则一次只适用于一个位置,而一致性规则适用于各个位置。在实践中,更重要的区别是内存一致性模型是唯一在架构上记录的模型。

简而言之,英特尔(和AMD同样)定义了一个特定的内存一致性模型x86-TSO3 - 就内存模型而言相对较强,但仍弱于{{3 }}。与顺序一致性相比,削弱的主要行为是:

  • 以后的负载可以通过早期的商店。
  • 可以看到商店的存储顺序与总商店订单的顺序不同,但只能由执行其中一个商店的核心查看。

为了实施这个内存模型,各个部分必须遵守规则来实现它。在所有最近的x86中,这意味着有序的加载和存储缓冲区,这避免了不允许的重新排序。使用存储缓冲区会导致上面提到的两个重新排序:不允许这些重新排序,实现将非常有限并且可能要慢得多。在实践中,它还意味着完全一致的数据缓存,因为如果没有这种保证,许多保证(例如,没有负载重新排序)将很难实现。

将它全部包装起来:

  • 内存一致性与缓存一致性不同:前者是记录的内容,是编程模型的一部分。
  • 在实践中,x86实现具有完全一致的缓存,这有助于他们实现x86-TSO内存模型,该模型相当强大但比顺序一致性弱。
  • 最后,或许你正在寻找的答案,换句话说:一个比顺序一致性弱的内存模型仍然非常有用,因为你可以对它进行编程,并且在某些特定操作需要顺序一致性的情况下你插入了正确的记忆障碍 4
  • 如果针对语言提供的内存模型(例如sequential consistencyJava's进行编程,则无需担心硬件细节,而是担心语言内存模型,编译器会插入将语言内存模型语义与硬件语义匹配所需的障碍。硬件模型越强,所需的障碍越少。

1 如果您的内存模型完全弱,即没有真正对跨核心重新排序施加任何限制,我想您可以直接在非核心重新排序上实现它以正常操作的廉价方式缓存一致系统,但随后内存障碍可能变得非常昂贵,因为它们需要刷新本地私有缓存的潜在大部分。

2 各种芯片可能在内部以不同方式实现,特别是一些芯片可能实现更强语义而不是模型(即,某些允许的重新排序永远不会被观察到),但没有错误,没有一个会实现一个较弱的。

3 这是我在论文中给出的名称,因为英特尔本身并没有给它起一个名字,而且这篇文章的定义比英特尔给出的更正式。作为一系列试金石的不太正式的模型。

4 它在x86上练习你通常使用锁定指令(使用lock前缀)而不是单独的障碍,尽管也存在独立的障碍。这里我将使用术语 barries 来引用嵌入锁定指令中的独立障碍和屏障语义。

答案 1 :(得分:2)

Re:你的编辑,这似乎是一个新问题:对,存储转发"违反"一致性。核心可以比任何其他核心更早看到自己的商店。存储缓冲区不一致。

x86内存排序规则要求加载按程序顺序全局可见,但允许内核在全局可见之前从其自己的商店加载数据。它没有pretend it waited and check for memory-order mis-speculation, like it does in other cases of doing loads earlier than the memory model says it should.

也相关; Can x86 reorder a narrow store with a wider load that fully contains it?是存储缓冲区+存储转发的一个特定示例,违反了通常的内存排序规则。请参阅Linus Torvalds的邮件列表帖子this collection,解释存储转发对内存排序的影响(以及它意味着提议的锁定方案不起作用)。

如果没有一致性,你将如何原子地增加共享计数器,或实现其他原子读 - 修改 - 写操作,这些操作对于实现锁或直接在无锁代码中使用是必不可少的。 (见Can num++ be atomic for 'int num'?)。

同时在多个线程中的

lock add [shared_counter], 1永远不会丢失对实际x86的任何计数,因为lock前缀使得核心保持从加载到缓存行的独占所有权,直到存储提交到L1d (从而变得全局可见)。

没有连贯缓存的系统会让每个线程递增自己的共享计数器副本,然后内存中的最终值将来自最后刷新该行的任何线程。

允许不同的缓存长期保存相同行的冲突数据,即使发生其他加载/存储,并且跨越内存障碍,也会产生各种奇怪现象。

这也违反了纯商店立即对其他内核可见的假设。如果你根本没有一致性,那么核心可以继续使用他们的共享变量的缓存副本。所以如果您希望读者注意到更新,那么在每次读取共享变量之前都必须clflush,这使得常见情况变得昂贵(自上次检查以来没有人修改过数据)。

MESI就像一个推送通知系统,而不是强迫每个读者在每次阅读时重新验证他们的缓存。

MESI(或一般的一致性)允许RCU(读取 - 复制 - 更新)之类的内容对于读取器(与单线程相比)的开销为零,在共享数据结构尚未进行的情况下改性即可。请参阅https://lwn.net/Articles/262464/https://en.wikipedia.org/wiki/Read-copy-update。基本思想是,编写器不是锁定数据结构,而是复制整个内容,修改副本,然后更新共享指针以指向新版本。所以读者总是完全等待;他们只是取消引用(原子)指针,数据在其L1d缓存中保持热点。

硬件支持的一致性非常有价值,几乎每个共享内存SMP架构都使用它。即使具有比x86更弱的内存排序规则的ISA,如PowerPC,也使用MESI。