在多处理器系统中,每个核心外部的内存在概念上是否总是平坦/统一/同步的?

cur*_*guy 5 memory cpu-architecture cpu-registers memory-barriers cpu-cache

由于等待全局状态的全局同步几乎会一直不必要地停止所有执行,因此多处理器系统会无序地执行“实际”内存操作(那些操作会影响最终执行,而不仅仅是推测执行)。另一方面,从每个L1高速缓存开始,从允许的行为角度来看,内存系统似乎是完全同步,一致且平坦的(允许语义)。显然,时间取决于缓存的大小和行为。

因此,在一个CPU上,一个极端被称为“寄存器”,根据定义,它们是私有的,而在另一个极端上,则存在共享的内存。令人遗憾的是,在具有特殊命名或寻址模式的寄存器的微不足道的空间之外,存储器始终是全局的,共享的和全局同步的,并且实际上完全受制于所有限制,即使该存储器用作未命名的寄存器也是如此。其目的是存储比少数寄存器中容纳的数据更多的数据,而不会被其他线程检查(除非使用ptrace进行调试,因为ptrace显然会停止,停止,序列化并存储执行的完整可观察状态)。

在现代计算机(现代=可以合理地支持C ++和Java的计算机)上,情况总是如此吗?

专用L1高速缓存为什么不为那些仅由特定内核使用的存储单元提供类似寄存器的语义?高速缓存必须跟踪共享的内存,无论如何。当需要对内存操作进行严格的全局排序时,不必暂停此类本地数据的内存操作,因为没有其他内核在观察它,并且如果需要,缓存可以暂停此类外部访问。高速缓存将只需要知道哪些存储单元是私有的(不可全局读取),直到出现混乱的操作停顿为止,这将使之保持一致(高速缓存可能需要一种方法来请求核心对操作进行序列化并发布一致的状态在记忆中)。

是否所有CPU都停滞不前并同步篱笆或同步操作上的所有内存访问?

内存可以用作几乎不受限制的寄存器资源吗?

Pet*_*des 3

实际上,与单处理器系统的设计方式相比,在没有其他线程访问的内存上运行的单核并不会为了维护全局内存语义而减慢太多速度。

但在大型多插槽系统(尤其是 x86)上,缓存一致性(窥探其他插槽)导致缓存未命中导致内存延迟比单插槽系统更严重的原因之一。(对于私有缓存中丢失的访问)。


是的,所有可以运行单个多线程程序的多核系统都使用 MESI 缓存一致性协议的某些变体,在所有内核之间具有一致的共享内存。(此规则的任何例外都被认为是外来的,必须专门编程。)

具有多个需要显式刷新的独立一致性域的大型系统更像是一个用于高效消息传递的紧密耦合集群,而不是 SMP 系统。(普通的 NUMA 多插槽系统缓存一致的:mov + mfence 在 NUMA 上安全吗?专门针对 x86 进行详细介绍。)


当某个核心的缓存行处于 MESI 已修改或独占状态时,它可以对其进行修改,而无需通知其他核心有关更改的信息。一个高速缓存中的 M 和 E 状态意味着系统中没有其他高速缓存具有该行的任何有效副本。但加载和存储仍然必须遵守内存模型,例如 x86 内核仍然必须按程序顺序将存储提交到 L1d 缓存。


L1d 和 L2 是现代 CPU 核心的一部分,但您说得对,L1d 实际上并未经过推测性修改。可以推测性地阅读

您所询问的大部分内容都是由具有存储转发功能的存储缓冲区处理的,允许执行存储/重新加载,而无需等待存储变得全局可见。

什么是存储缓冲区?以及英特尔硬件上存储缓冲区的大小?存储缓冲区到底是什么?

存储缓冲区对于将推测性乱序执行(将数据+地址写入存储缓冲区)从按序提交解耦到全局可见的 L1d 缓存至关重要。

即使对于有序核心来说,这也非常重要,否则缓存未命中存储将停止执行。通常,您希望存储缓冲区将连续的窄存储合并为单个更宽的缓存写入,特别是对于可以积极执行此操作的弱排序 uarch;许多非 x86 微架构仅对对齐的 4 字节或更宽的块进行完全有效的缓存提交。

在强有序内存模型上,推测性乱序加载并稍后检查是否有任何其他核心在我们“允许”读取该行之前使该行无效,这对于高性能也至关重要,从而允许命中/未命中让无序执行继续,而不是一个缓存未命中加载导致所有其他加载停止。


该模型有一些限制:

  • 有限的存储缓冲区大小意味着我们没有太多的私人存储/重新加载空间
  • 强有序内存模型会阻止私有存储无序地提交到 L1d,因此存储到必须等待来自另一个核心的行的共享变量可能会导致存储缓冲区被私有存储填满。
  • mfence像 x86或lock add或 ARM这样的内存屏障指令dsb ish必须耗尽存储缓冲区,因此存储到实际上不共享的线程私有内存(并从中重新加载)仍然必须等待您关心的存储变得全局可见。
  • 相反,等待您关心的共享存储变得可见(使用屏障或释放存储)也必须等待私有内存操作,即使它们是独立的。