首页 > 解决方案 > 在多处理器系统中,每个内核之外的内存在概念上是否总是平坦/统一/同步?

问题描述

多处理器系统无序和异步地执行“真正的”内存操作(那些影响最终执行的操作,而不仅仅是推测执行),因为等待全局状态的全局同步将不必要地停止几乎所有时间的所有执行。另一方面,就在每个单独的核心之外,从 L1 缓存开始的内存系统似乎是纯同步的、一致的、从允许的行为角度来看是平坦的(允许的语义);显然时间取决于缓存大小和行为。

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

现代计算机(现代=那些可以合理支持 C++ 和 Java 的计算机)总是这样吗?

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

是否所有 CPU 都停止并同步栅栏上的所有内存访问或同步操作?

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

标签: memorycpu-architecturecpu-registerscpu-cachememory-barriers

解决方案


在实践中,在没有其他线程访问的内存上运行的单核不会因为维护全局内存语义而减慢很多,与如何设计单处理器系统相比。

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


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

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


当一个核心有一个处于 MESI Modified 或 Exclusive 状态的缓存行时,它可以修改它而不通知其他核心有关更改。一个高速缓存中的 M 和 E 状态意味着系统中没有其他高速缓存具有该行的任何有效副本。但是加载和存储仍然必须尊重内存模型,例如 x86 内核仍然必须按照程序顺序将存储提交到 L1d 缓存。


L1d 和 L2 是现代 CPU 内核的一部分,但你说得对,L1d 实际上并没有被投机修改。它可以推测性地阅读。

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

什么是存储缓冲区?英特尔硬件上的存储缓冲区大小?究竟什么是存储缓冲区?

存储缓冲区对于将推测性乱序执行(将数据+地址写入存储缓冲区)从按顺序提交到全局可见的 L1d 缓存中解耦是必不可少的。

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

在强排序内存模型上,推测性乱序加载并稍后检查是否有任何其他内核在我们被“允许”读取之前使该行无效,这对于高性能也是必不可少的,允许命中未命中让无序的 exec 继续,而不是一个缓存未命中加载停止所有其他加载。


这个模型有一些限制:

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

推荐阅读