L1 缓存控制器处理来自 CPU 的内存请求的顺序
The ordering of L1 cache controller to process memory requests from CPU
在总存储顺序 (TSO) 内存一致性模型下,x86 cpu 将有一个写入缓冲区来缓冲写入请求,并可以从写入缓冲区为重新排序的读取请求提供服务。并且它说写缓冲区中的写请求将退出并以FIFO顺序向缓存层次结构发出,这与程序顺序相同。
我很好奇:
为了服务从写缓冲区发出的写请求,一级缓存控制器是否处理写请求,完成写请求的缓存一致性,并按照与发出顺序相同的顺序将数据插入一级缓存?
你的术语很不寻常。你说“完成缓存一致性”;实际发生的是核心必须在 可以修改它之前 获得缓存行的(独占)所有权。在 instant/cycle 发生修改时,它成为缓存一致性协议中所有参与者共享的内存内容视图的一部分。
所以,是的,您确实“完成了缓存一致性”= 获得独占所有权 在 商店甚至可以进入缓存并变得全局可见 = 可用于共享该缓存行的请求.缓存始终保持一致性(这是 MESI 的重点),而不是不同步然后等待一致性。我认为你的困惑源于你的心理模型不与现实相匹配。
(弱序架构具有令人费解的可能性,例如并非所有核心都以相同的顺序从其他两个核心看到存储;这可能会在 之前发生。)
我想你知道一些,但让我从基础开始。
每个内核中的 L1 缓存参与缓存一致性协议,该协议使其缓存与一致性域中的其他缓存保持一致(例如 L2 和 L3,以及其他内核中的 L1,但不是视频 RAM 缓存内部) GPU).
加载在从 L1 缓存中读取数据时变得全局可见 ( or from uncacheable RAM or MMIO). MFENCE
can force them to wait for earlier stores to become globally visible before sampling L1, to avoid StoreLoad reordering.
存储在其数据提交到 L1 缓存的那一刻就变得全局可见。发生这种情况所需的条件是:
执行完毕:数据+地址在存储缓冲区条目中。 (即一旦输入准备就绪,存储地址和存储数据 uops 在适当的端口上执行,将地址和数据写入存储缓冲区,也就是 Intel CPU 上的内存顺序缓冲区)。
它是 retired from the out-of-order part of the core, and thus known to be non-speculative. Before retirement, we don't know that it and all preceding instructions won't fault,或者它不在分支预测错误或其他错误推测的阴影下。
退休只能在它完成执行后发生,但与对 L1d 的承诺无关。存储缓冲区可以继续跟踪非推测性存储,即使在 ROB(乱序执行重新排序缓冲区)忘记了存储 指令.
前面的所有 loads/stores/fences 已经全局可见(因为 x86 的内存排序规则)。这不包括弱排序操作(NT 存储);其他loads/stores可以通过。
缓存行处于MESI/MESIF/MOESI缓存一致性协议的Exclusive或Modified状态,在当前核心的L1d缓存中。 如果 RFO(读取所有权)在缓存的外部级别遇到缓存未命中,或者与其他也希望独占访问写入或原子 RMW 缓存行的内核争用,这可能需要很长时间。
有关允许的状态转换图和详细信息,请参阅维基百科的 MESI article。关键点是一致性是通过只允许核心修改它的缓存行副本来实现的,当它确定没有其他缓存包含该行时,所以两个冲突的副本是不可能的存在同一行。
Intel CPU 实际上使用 MESIF, while AMD CPUs actually use MOESI 允许脏数据的缓存-> 缓存数据传输,而不是像基本 MESI 协议要求的那样写回共享外部缓存。
另请注意,现代英特尔设计(在 Skylake-AVX512 之前)实现使用 large shared inclusive L3 cache as a backstop for cache-coherency,因此探听请求实际上不必广播到所有内核;他们只是检查 L3 标签(其中包含额外的元数据来跟踪哪个内核正在缓存什么。
Intel 的 L3 是包含标签的,即使对于内部高速缓存处于排他或修改状态且因此在 L3 中无效的行也是如此。参见 this paper for more details of a simplified version of what Intel does)。
也相关:I wrote an answer recently about why we have small/fast L1 + larger L2/L3, instead of one big cache,包括一些指向其他缓存相关内容的链接。
回到正题:
是的,存储按程序顺序提交到 L1 ,因为这是 x86 要求它们变得全局可见的顺序。 L1 提交顺序与全局可见性顺序相同。
而不是“完成缓存一致性”,而应该说“获得缓存行的所有权”。这涉及使用缓存一致性协议与其他缓存进行通信,所以我猜你的意思可能是“使用缓存一致性协议完成获得独占所有权”。
MESI wiki 文章的 memory ordering 部分指出,存储队列中的缓冲存储与一般的乱序执行是分开的。
存储缓冲区将对 L1d 的提交与 OoO exec 退休分离。这可能会隐藏 lot 比常规无序 window 大小更多的存储延迟。然而,退役存储 必须 最终发生(以正确的顺序),即使中断到达,因此允许大量退役但未提交的存储会增加中断延迟。
存储缓冲区尝试尽快将退役的存储提交给 L1d,但它受到内存排序规则的限制。 (即其他核心很快就会看到存储;您不需要栅栏来刷新存储缓冲区,除非您需要当前线程等待它发生,然后再在此线程中加载。例如,对于顺序一致的存储。)
在弱排序的 ISA 上,较晚的存储可以提交到 L1d,而较早的存储仍在等待缓存未命中。 (但是您仍然需要一个内存顺序缓冲区来保留程序顺序中单核 运行 指令的错觉。)
存储缓冲区可以同时有多个缓存未命中,因为即使在强顺序 x86 上,它也可以在该存储是缓冲区中最旧的缓存行之前发送一个缓存行的 RFO。
是的 在像 x86-TSO 这样的模型中,存储很可能按程序顺序提交给 L1,并且 很好地涵盖了它。也就是说,存储缓冲区按程序顺序维护,在继续之前,核心只会将最旧的存储(或者可能是几个连续的最旧存储,如果它们都进入同一缓存行)提交到 L1。 1
但是,您在评论中提到您担心这可能会影响性能,因为这实际上会使存储缓冲区提交一个阻塞(序列化)进程:
And why I am confused about this problem is that cache controller
could handle the requests in a non-blocking way. But, to conform to
the TSO and make sure data globally visible on a multi-core system,
should cache controller follow the store ordering? Because if there
are two variable A and B being updated sequentially on core 1 and core
2 get the updated B from core 1, then core 2 must also can see the
updated A. And to achieve this, I think the private cache hierarchy on
core 1 have to finishes the cache coherence of the variable A and B in
order and make them globally visible. Am I right?
好消息是,即使存储缓冲区可能仅以有序方式将最旧的存储提交到 L1,它仍然可以通过在存储中向前看,相对于内存子系统的其余部分获得足够的并行性缓冲区并发出 prefetch RFO 请求:尝试在本地核心中获取处于 E
状态的行,甚至在存储首先提交到 L1 之前。
这种方法不违反顺序,因为存储仍然按程序顺序编写,但它在解决 L1 存储未命中时允许完全并行。无论如何,真正重要的是 L1 存储未命中:L1 中的存储命中可以快速提交,每个周期至少 1 个,因此提交一堆命中并没有多大帮助:但是在存储未命中上获得 MLP 非常重要,尤其是对于分散的存储预取器无法处理。
x86 芯片真的使用这样的技术吗?几乎可以确定。最令人信服的是,对一长串随机写入的测试显示平均延迟比完整内存延迟要好得多,这意味着 MLP 明显优于一个。您还可以找到像 this one or this one 这样的专利,其中英特尔几乎完全描述了这种方法。
不过,没有什么是完美的。有一些证据表明,当商店缺少 L1 时,订购问题会导致 weird performance hiccups,即使它们进入了 L2。
1 当然 可能 如果保持 错觉 [=] 它可以乱序提交存储38=] 的顺序提交,例如,在恢复顺序之前不放弃乱序写入的缓存行的所有权,但这很容易出现死锁和其他复杂情况,我没有证据表明 x86 会这样做。
在总存储顺序 (TSO) 内存一致性模型下,x86 cpu 将有一个写入缓冲区来缓冲写入请求,并可以从写入缓冲区为重新排序的读取请求提供服务。并且它说写缓冲区中的写请求将退出并以FIFO顺序向缓存层次结构发出,这与程序顺序相同。
我很好奇:
为了服务从写缓冲区发出的写请求,一级缓存控制器是否处理写请求,完成写请求的缓存一致性,并按照与发出顺序相同的顺序将数据插入一级缓存?
你的术语很不寻常。你说“完成缓存一致性”;实际发生的是核心必须在 可以修改它之前 获得缓存行的(独占)所有权。在 instant/cycle 发生修改时,它成为缓存一致性协议中所有参与者共享的内存内容视图的一部分。
所以,是的,您确实“完成了缓存一致性”= 获得独占所有权 在 商店甚至可以进入缓存并变得全局可见 = 可用于共享该缓存行的请求.缓存始终保持一致性(这是 MESI 的重点),而不是不同步然后等待一致性。我认为你的困惑源于你的心理模型不与现实相匹配。
(弱序架构具有令人费解的可能性,例如并非所有核心都以相同的顺序从其他两个核心看到存储;这可能会在
我想你知道一些,但让我从基础开始。
每个内核中的 L1 缓存参与缓存一致性协议,该协议使其缓存与一致性域中的其他缓存保持一致(例如 L2 和 L3,以及其他内核中的 L1,但不是视频 RAM 缓存内部) GPU).
加载在从 L1 缓存中读取数据时变得全局可见 (MFENCE
can force them to wait for earlier stores to become globally visible before sampling L1, to avoid StoreLoad reordering.
存储在其数据提交到 L1 缓存的那一刻就变得全局可见。发生这种情况所需的条件是:
执行完毕:数据+地址在存储缓冲区条目中。 (即一旦输入准备就绪,存储地址和存储数据 uops 在适当的端口上执行,将地址和数据写入存储缓冲区,也就是 Intel CPU 上的内存顺序缓冲区)。
它是 retired from the out-of-order part of the core, and thus known to be non-speculative. Before retirement, we don't know that it and all preceding instructions won't fault,或者它不在分支预测错误或其他错误推测的阴影下。
退休只能在它完成执行后发生,但与对 L1d 的承诺无关。存储缓冲区可以继续跟踪非推测性存储,即使在 ROB(乱序执行重新排序缓冲区)忘记了存储 指令.
前面的所有 loads/stores/fences 已经全局可见(因为 x86 的内存排序规则)。这不包括弱排序操作(NT 存储);其他loads/stores可以通过。
缓存行处于MESI/MESIF/MOESI缓存一致性协议的Exclusive或Modified状态,在当前核心的L1d缓存中。 如果 RFO(读取所有权)在缓存的外部级别遇到缓存未命中,或者与其他也希望独占访问写入或原子 RMW 缓存行的内核争用,这可能需要很长时间。
有关允许的状态转换图和详细信息,请参阅维基百科的 MESI article。关键点是一致性是通过只允许核心修改它的缓存行副本来实现的,当它确定没有其他缓存包含该行时,所以两个冲突的副本是不可能的存在同一行。
Intel CPU 实际上使用 MESIF, while AMD CPUs actually use MOESI 允许脏数据的缓存-> 缓存数据传输,而不是像基本 MESI 协议要求的那样写回共享外部缓存。
另请注意,现代英特尔设计(在 Skylake-AVX512 之前)实现使用 large shared inclusive L3 cache as a backstop for cache-coherency,因此探听请求实际上不必广播到所有内核;他们只是检查 L3 标签(其中包含额外的元数据来跟踪哪个内核正在缓存什么。
Intel 的 L3 是包含标签的,即使对于内部高速缓存处于排他或修改状态且因此在 L3 中无效的行也是如此。参见 this paper for more details of a simplified version of what Intel does)。
也相关:I wrote an answer recently about why we have small/fast L1 + larger L2/L3, instead of one big cache,包括一些指向其他缓存相关内容的链接。
回到正题:
是的,存储按程序顺序提交到 L1 ,因为这是 x86 要求它们变得全局可见的顺序。 L1 提交顺序与全局可见性顺序相同。
而不是“完成缓存一致性”,而应该说“获得缓存行的所有权”。这涉及使用缓存一致性协议与其他缓存进行通信,所以我猜你的意思可能是“使用缓存一致性协议完成获得独占所有权”。
MESI wiki 文章的 memory ordering 部分指出,存储队列中的缓冲存储与一般的乱序执行是分开的。
存储缓冲区将对 L1d 的提交与 OoO exec 退休分离。这可能会隐藏 lot 比常规无序 window 大小更多的存储延迟。然而,退役存储 必须 最终发生(以正确的顺序),即使中断到达,因此允许大量退役但未提交的存储会增加中断延迟。
存储缓冲区尝试尽快将退役的存储提交给 L1d,但它受到内存排序规则的限制。 (即其他核心很快就会看到存储;您不需要栅栏来刷新存储缓冲区,除非您需要当前线程等待它发生,然后再在此线程中加载。例如,对于顺序一致的存储。)
在弱排序的 ISA 上,较晚的存储可以提交到 L1d,而较早的存储仍在等待缓存未命中。 (但是您仍然需要一个内存顺序缓冲区来保留程序顺序中单核 运行 指令的错觉。)
存储缓冲区可以同时有多个缓存未命中,因为即使在强顺序 x86 上,它也可以在该存储是缓冲区中最旧的缓存行之前发送一个缓存行的 RFO。
是的 在像 x86-TSO 这样的模型中,存储很可能按程序顺序提交给 L1,并且
但是,您在评论中提到您担心这可能会影响性能,因为这实际上会使存储缓冲区提交一个阻塞(序列化)进程:
And why I am confused about this problem is that cache controller could handle the requests in a non-blocking way. But, to conform to the TSO and make sure data globally visible on a multi-core system, should cache controller follow the store ordering? Because if there are two variable A and B being updated sequentially on core 1 and core 2 get the updated B from core 1, then core 2 must also can see the updated A. And to achieve this, I think the private cache hierarchy on core 1 have to finishes the cache coherence of the variable A and B in order and make them globally visible. Am I right?
好消息是,即使存储缓冲区可能仅以有序方式将最旧的存储提交到 L1,它仍然可以通过在存储中向前看,相对于内存子系统的其余部分获得足够的并行性缓冲区并发出 prefetch RFO 请求:尝试在本地核心中获取处于 E
状态的行,甚至在存储首先提交到 L1 之前。
这种方法不违反顺序,因为存储仍然按程序顺序编写,但它在解决 L1 存储未命中时允许完全并行。无论如何,真正重要的是 L1 存储未命中:L1 中的存储命中可以快速提交,每个周期至少 1 个,因此提交一堆命中并没有多大帮助:但是在存储未命中上获得 MLP 非常重要,尤其是对于分散的存储预取器无法处理。
x86 芯片真的使用这样的技术吗?几乎可以确定。最令人信服的是,对一长串随机写入的测试显示平均延迟比完整内存延迟要好得多,这意味着 MLP 明显优于一个。您还可以找到像 this one or this one 这样的专利,其中英特尔几乎完全描述了这种方法。
不过,没有什么是完美的。有一些证据表明,当商店缺少 L1 时,订购问题会导致 weird performance hiccups,即使它们进入了 L2。
1 当然 可能 如果保持 错觉 [=] 它可以乱序提交存储38=] 的顺序提交,例如,在恢复顺序之前不放弃乱序写入的缓存行的所有权,但这很容易出现死锁和其他复杂情况,我没有证据表明 x86 会这样做。