在此示例中,数据缓存如何路由对象?
How do data caches route the object in this example?
考虑图表数据缓存架构。 (ASCII 艺术如下。)
--------------------------------------
| CPU core A | CPU core B | |
|------------|------------| Devices |
| Cache A1 | Cache B1 | with DMA |
|-------------------------| |
| Cache 2 | |
|------------------------------------|
| RAM |
--------------------------------------
假设
- 一个对象隐藏在缓存 A1 的 脏 行,
- 同一对象的旧版本隐藏在缓存 2 的 clean 行中,并且
- 同一对象的最新版本最近已通过 DMA 写入 RAM。
图表:
--------------------------------------
| CPU core A | CPU core B | |
|------------|------------| Devices |
| (dirty) | | with DMA |
|-------------------------| |
| (older, clean) | |
|------------------------------------|
| (newest, via DMA) |
--------------------------------------
请问三个问题。
如果 CPU 核心 A 尝试加载(读取)对象,会发生什么?
如果 CPU 核心 A 尝试存储(写入)对象,会发生什么情况?
如果加载或存储的不是核心 A,而是核心 B,是否会发生任何不明显、有趣的 and/or 不同?
我的问题是理论性的。我的问题不涉及任何特定的 CPU 架构,但如果您愿意,您可以在回答中提及 x86 或 ARM(甚至 RISC-V)。
注释。如果忽略窥探会简化您的答案,那么您可以自行决定忽略窥探。或者,如果修改后的问题能更好地阐明您认为的主题,您可以修改问题。如果一定要写代码回答,那我宁愿C/C++。据我所知,您不需要在回答中指定 MESI 或 MOESI 协议的特定标志,但一个更简单、不太详细的回答可能就足够了。
Motive. 我想问的动机是我正在阅读有关 C++ 标准中的并发和内存模型的内容。如果可能的话,我想学习在硬件操作方面大致可视化这个模型。
更新
就我的理解而言,@HadiBrais 建议以下图表架构比我之前绘制的架构更常见,尤其是在实施 DDIO(请参阅下面他的回答)的情况下。
--------------------------------------
| CPU core A | CPU core B | Devices |
|------------|------------| with DMA |
| Cache A1 | Cache B1 | |
|------------------------------------|
| Cache 2 |
|------------------------------------|
| RAM |
--------------------------------------
您假设的系统似乎包括连贯的回写式 L1 高速缓存和非连贯的 DMA。一个非常相似的真实处理器是 ARM11 MPCore,只是它没有 L2 缓存。然而,大多数现代处理器确实具有相干 DMA。否则,确保一致性是软件的责任。图表中显示的系统状态已经不连贯。
If CPU core A tries to load (read) the object, what happens?
它只会读取保存在其本地 L1 缓存中的行。不会发生任何变化。
If, instead, CPU core A tries to store (write) the object, what
happens?
这些行在核心A的L1缓存中已经处于M一致性状态。因此可以直接写入。不会发生任何变化。
Would anything nonobvious, interesting and/or different happen if,
rather than core A, core B did the loading or storing?
如果核心B向同一行发出加载请求,则核心A的L1缓存被窥探,发现该行处于M状态。该行在 L2 缓存中更新并发送到核心 B 的 L1 缓存。还会发生以下情况之一:
- 该行从核心 A 的 L1 缓存中无效。该行在E相干态(MESI协议的情况下)或S相干态(MSI协议的情况下)插入核心B的L1缓存。如果 L2 使用探听过滤器,则更新过滤器以指示内核 B 的线路处于 E/S 状态。否则,L2 中线路的状态将与核心 B 的 L1 中的线路状态相同,只是它不知道它在那里(因此必须始终广播窥探)。
- 线在核心A的一级缓存中的状态变为S。线以S一致性状态插入核心B的一级缓存中。 L2在S状态插入线。
无论哪种方式,L1 缓存和 L2 缓存都将保存同一行的副本,这与内存中的副本保持不一致。
如果核心 B 向同一行发出存储请求,则该行将从核心 A 的缓存中失效,并最终在核心 B 的缓存中处于 M 状态。
最终,该行将从缓存层次结构中逐出,为其他行创建 space。发生这种情况时,有两种情况:
- 该行处于 S/E 状态,因此它会从所有缓存中删除。后面如果再次读取该行,会从主存中读取DMA操作写入的副本。
- 该行处于 M 状态,因此它将被写回主内存并(可能部分)覆盖由 DMA 操作写入的副本。
显然,这种不连贯的状态绝不能发生。可以通过在 DMA 写入操作开始之前使所有缓存中的所有相关行无效并确保在操作完成之前没有内核访问正在写入的内存区域来防止这种情况发生。每当操作完成时,DMA 控制器都会发送一个中断。在读取 DMA 操作的情况下,所有相关行都需要写回内存以确保使用最新的值。
Intel Data Direct I/O(DDIO)技术使DMA控制器可以直接从共享末级缓存中读取或写入,从而提高性能。
这部分与问题没有直接关系,但我想把它写在某个地方。
所有商业 x86 CPU 都是完全缓存一致的(即,整个缓存层次结构是一致的)。更准确地说,同一共享内存域中的所有处理器都是高速缓存一致的。此外,所有商用 x86 众核协处理器(即 PCIe 卡形式的英特尔至强融核)内部 完全一致。协处理器是 PCIe 互连上的设备,与其他协处理器或 CPU 不一致。因此,协处理器驻留在它自己的一个单独的一致性域中。我认为这是因为没有内置的硬件机制来使具有高速缓存的 PCIe 设备与其他 PCIe 设备或 CPU 保持一致。
除了商用 x86 芯片,还有缓存不一致的原型 x86 芯片。我所知道的唯一例子是 Intel 的 Single-Chip Cloud Computer (SCC),它后来演变成连贯的 Xeon Phi。
考虑图表数据缓存架构。 (ASCII 艺术如下。)
--------------------------------------
| CPU core A | CPU core B | |
|------------|------------| Devices |
| Cache A1 | Cache B1 | with DMA |
|-------------------------| |
| Cache 2 | |
|------------------------------------|
| RAM |
--------------------------------------
假设
- 一个对象隐藏在缓存 A1 的 脏 行,
- 同一对象的旧版本隐藏在缓存 2 的 clean 行中,并且
- 同一对象的最新版本最近已通过 DMA 写入 RAM。
图表:
--------------------------------------
| CPU core A | CPU core B | |
|------------|------------| Devices |
| (dirty) | | with DMA |
|-------------------------| |
| (older, clean) | |
|------------------------------------|
| (newest, via DMA) |
--------------------------------------
请问三个问题。
如果 CPU 核心 A 尝试加载(读取)对象,会发生什么?
如果 CPU 核心 A 尝试存储(写入)对象,会发生什么情况?
如果加载或存储的不是核心 A,而是核心 B,是否会发生任何不明显、有趣的 and/or 不同?
我的问题是理论性的。我的问题不涉及任何特定的 CPU 架构,但如果您愿意,您可以在回答中提及 x86 或 ARM(甚至 RISC-V)。
注释。如果忽略窥探会简化您的答案,那么您可以自行决定忽略窥探。或者,如果修改后的问题能更好地阐明您认为的主题,您可以修改问题。如果一定要写代码回答,那我宁愿C/C++。据我所知,您不需要在回答中指定 MESI 或 MOESI 协议的特定标志,但一个更简单、不太详细的回答可能就足够了。
Motive. 我想问的动机是我正在阅读有关 C++ 标准中的并发和内存模型的内容。如果可能的话,我想学习在硬件操作方面大致可视化这个模型。
更新
就我的理解而言,@HadiBrais 建议以下图表架构比我之前绘制的架构更常见,尤其是在实施 DDIO(请参阅下面他的回答)的情况下。
--------------------------------------
| CPU core A | CPU core B | Devices |
|------------|------------| with DMA |
| Cache A1 | Cache B1 | |
|------------------------------------|
| Cache 2 |
|------------------------------------|
| RAM |
--------------------------------------
您假设的系统似乎包括连贯的回写式 L1 高速缓存和非连贯的 DMA。一个非常相似的真实处理器是 ARM11 MPCore,只是它没有 L2 缓存。然而,大多数现代处理器确实具有相干 DMA。否则,确保一致性是软件的责任。图表中显示的系统状态已经不连贯。
If CPU core A tries to load (read) the object, what happens?
它只会读取保存在其本地 L1 缓存中的行。不会发生任何变化。
If, instead, CPU core A tries to store (write) the object, what happens?
这些行在核心A的L1缓存中已经处于M一致性状态。因此可以直接写入。不会发生任何变化。
Would anything nonobvious, interesting and/or different happen if, rather than core A, core B did the loading or storing?
如果核心B向同一行发出加载请求,则核心A的L1缓存被窥探,发现该行处于M状态。该行在 L2 缓存中更新并发送到核心 B 的 L1 缓存。还会发生以下情况之一:
- 该行从核心 A 的 L1 缓存中无效。该行在E相干态(MESI协议的情况下)或S相干态(MSI协议的情况下)插入核心B的L1缓存。如果 L2 使用探听过滤器,则更新过滤器以指示内核 B 的线路处于 E/S 状态。否则,L2 中线路的状态将与核心 B 的 L1 中的线路状态相同,只是它不知道它在那里(因此必须始终广播窥探)。
- 线在核心A的一级缓存中的状态变为S。线以S一致性状态插入核心B的一级缓存中。 L2在S状态插入线。
无论哪种方式,L1 缓存和 L2 缓存都将保存同一行的副本,这与内存中的副本保持不一致。
如果核心 B 向同一行发出存储请求,则该行将从核心 A 的缓存中失效,并最终在核心 B 的缓存中处于 M 状态。
最终,该行将从缓存层次结构中逐出,为其他行创建 space。发生这种情况时,有两种情况:
- 该行处于 S/E 状态,因此它会从所有缓存中删除。后面如果再次读取该行,会从主存中读取DMA操作写入的副本。
- 该行处于 M 状态,因此它将被写回主内存并(可能部分)覆盖由 DMA 操作写入的副本。
显然,这种不连贯的状态绝不能发生。可以通过在 DMA 写入操作开始之前使所有缓存中的所有相关行无效并确保在操作完成之前没有内核访问正在写入的内存区域来防止这种情况发生。每当操作完成时,DMA 控制器都会发送一个中断。在读取 DMA 操作的情况下,所有相关行都需要写回内存以确保使用最新的值。
Intel Data Direct I/O(DDIO)技术使DMA控制器可以直接从共享末级缓存中读取或写入,从而提高性能。
这部分与问题没有直接关系,但我想把它写在某个地方。
所有商业 x86 CPU 都是完全缓存一致的(即,整个缓存层次结构是一致的)。更准确地说,同一共享内存域中的所有处理器都是高速缓存一致的。此外,所有商用 x86 众核协处理器(即 PCIe 卡形式的英特尔至强融核)内部 完全一致。协处理器是 PCIe 互连上的设备,与其他协处理器或 CPU 不一致。因此,协处理器驻留在它自己的一个单独的一致性域中。我认为这是因为没有内置的硬件机制来使具有高速缓存的 PCIe 设备与其他 PCIe 设备或 CPU 保持一致。
除了商用 x86 芯片,还有缓存不一致的原型 x86 芯片。我所知道的唯一例子是 Intel 的 Single-Chip Cloud Computer (SCC),它后来演变成连贯的 Xeon Phi。