一级缓存控制器处理来自CPU的内存请求的顺序



在总存储顺序(TSO)内存一致性模型下,x86 cpu将有一个写缓冲区来缓冲写请求,并可以为来自写缓冲区的重新排序的读请求提供服务。它说写缓冲区中的写请求将退出,并按FIFO顺序向缓存层次结构发出,这与程序顺序相同。

我很好奇:

为了服务从写缓冲器发出的写请求,一级缓存控制器是否处理写请求,完成写请求的缓存一致性,并按照与发出顺序相同的顺序将数据插入一级缓存?

您的术语不同寻常。你说";完成高速缓存一致性";;实际发生的情况是,核心必须获得缓存线的(独占)所有权,然后才能修改它。在修改发生的瞬间/周期,它成为缓存一致性协议中所有参与者共享的内存内容视图的一部分。

所以,是的,你会;完成高速缓存一致性"=在之前获得独占所有权存储甚至可以进入缓存并变得全局可见=可用于共享该缓存行的请求。缓存始终保持一致性(这是MESI的重点),而不是失去同步然后等待一致性。我认为你的困惑源于你的心理模型与现实不匹配。

(弱序体系结构具有令人费解的可能性,比如并非所有核心都能以相同的顺序从另外两个核心看到存储;这可能是通过一个物理核心上的SMT线程之间的私有存储转发,让另一个逻辑核心在提交L1d=全局可见性之前看到存储来实现的。)


我想你知道其中的一些,但让我从基础开始。

每个核中的L1高速缓存参与高速缓存一致性协议,该协议保持其高速缓存与一致性域中的其他高速缓存一致(例如,L2和L3,以及其他核中的L2,但不包括GPU内的视频RAM高速缓存)。

加载在其数据从一级缓存(或从存储缓冲区或不可缓存的RAM或MMIO)读取的瞬间变得全局可见。MFENCE可以强制它们在采样L1之前等待较早的存储变得全局可见,以避免StoreLoad重新排序。

存储在其数据提交到一级缓存的瞬间变得全局可见。发生这种情况之前需要的条件是:

  • 执行完毕:数据+地址在存储缓冲区条目中。(即,存储地址和存储数据在输入准备就绪后在适当的端口上执行,将地址和数据写入存储缓冲区,即英特尔CPU上的内存顺序缓冲区)。

  • 它从核心的无序部分退出,因此被认为是非推测性的。在退休之前,我们不知道它和之前的所有指令都不会出错,也不知道它不是在分支预测失误或其他错误猜测的阴影下。

    退休只能在它完成执行后发生,但与对L1d的承诺无关。存储缓冲区可以继续跟踪非推测性存储,即使在ROB(无序执行ReOrder buffer)忘记存储指令之后,这种存储最终也肯定会发生。

  • 所有先前的加载/存储/围栏都已全局可见(因为x86的内存排序规则)。这不包括弱有序操作(NT存储);其他加载/存储可以通过它们。

  • 在当前核心的L1d缓存中,缓存行处于MESI/MESIF/MOSI缓存一致性协议的Exclusive或Modified状态如果RFO(读取所有权)在缓存的外部级别遇到缓存未命中,或者与其他也希望独占访问写缓存线的核心发生争用,或者原子RMW缓存线,则这可能需要很长时间。

有关允许的状态转换的图表和详细信息,请参阅维基百科的MESI文章。关键是,只有在确定没有其他缓存包含缓存行时,才允许核心修改缓存行的副本,从而实现一致性,这样就不可能存在同一行的两个冲突副本。

英特尔CPU实际上使用MESIF,而AMD CPU实际上使用MOESI来允许缓存->缓存脏数据的数据传输,而不是像基本MESI协议所要求的那样写回共享的外部缓存。

还要注意的是,现代英特尔设计(在Skylake-VX512之前)使用大型共享包含的L3缓存作为缓存一致性的后盾,因此窥探请求实际上不必广播到所有内核;他们只检查L3标记(其中包含额外的元数据,以跟踪哪个核心正在缓存什么。
Intel的L3包含标记,即使内部缓存处于Exclusive或Modified状态,因此在L3中无效的行也是如此。请参阅本文,了解英特尔简化版的更多详细信息)。

还相关:我最近写了一个答案,解释为什么我们有小/快的L1+大的L2/L3,而不是一个大的缓存,包括一些到其他缓存相关内容的链接。


回到实际问题:

是的,存储按程序顺序提交到L1,因为这是x86要求它们全局可见的顺序。一级提交顺序与全局可见性顺序相同。

代替";完成高速缓存一致性";,相反,你应该说";获得高速缓存行的所有权";。这涉及到使用缓存一致性协议与其他缓存进行通信,所以我想你可能是指";完成使用高速缓存一致性协议的独占所有权";。

MESIWiki文章的内存排序部分指出,存储队列中的缓冲存储通常与无序执行是分开的。

存储缓冲区将对L1d的提交与OoO exec退休解耦。这可能会隐藏比常规无序窗口大小更多的存储延迟。然而,即使中断到达,失效存储也必须最终发生(按正确顺序),因此允许大量失效但未提交的存储可能会增加中断延迟。

存储缓冲区试图尽可能快地将失效的存储提交到L1d,但它受到内存排序规则的限制。(也就是说,其他核心很快就会看到存储;你不需要围栏来刷新存储缓冲区,除非你需要当前线程在稍后加载该线程之前等待。例如,对于顺序一致的存储。)

在弱序ISA上,后面的存储可以提交到L1d,而前面的存储仍在等待缓存未命中

存储缓冲区可能同时有多个缓存未命中,因为即使在强有序x86上,它也可以在缓存线成为缓冲区中最旧的缓存线之前发送该缓存线的RFO。

在x86这样的模型中,TSO存储很可能按程序顺序提交到L1,Peter的回答很好地涵盖了这一点。也就是说,存储缓冲区是按程序顺序维护的,并且核心在继续前进之前只将最旧的存储(或者如果它们都去到同一高速缓存行,则可能是几个连续的最旧存储)提交给L1

然而,您在评论中提到,您担心这可能会通过使存储缓冲区提交阻塞(序列化)过程来影响性能:

我对这个问题感到困惑的原因是缓存控制器可以以非阻塞的方式处理请求。但是,为了符合TSO,并确保数据在多核系统上全局可见,缓存控制器是否应该遵循存储顺序?因为如果有是在核心1和核心上顺序更新的两个变量A和B2从核心1获得更新的B,那么核心2也必须可以看到更新了A.为了实现这一点,我认为核心1必须完成中变量A和B的高速缓存一致性订购并使其在全球可见。我说得对吗?

好消息是,即使存储缓冲区可能以有序的方式仅将最旧的存储提交到L1,它仍然可以通过在存储缓冲区中向前看并发出预取RFO请求来获得与存储器子系统的其余部分的大量并行性:即使在存储第一个提交到L1之前,也尝试在本地核心中使行处于E状态。

这种方法不会违反排序,因为存储仍然是按程序顺序编写的,但在解决一级存储未命中时,它允许完全并行。无论如何,一级存储未命中才是真正重要的:一级存储中的存储命中可以快速提交,每个周期至少提交一次,所以提交一堆命中并没有多大帮助:但获得存储未命中的MLP非常重要,尤其是对于预取器无法处理的分散存储。

x86芯片真的使用这样的技术吗?几乎可以肯定。最令人信服的是,对一系列随机写入的测试显示,平均延迟比全内存延迟好得多,这意味着MLP明显优于1。你也可以找到像这个或这个专利,英特尔几乎完全描述了这种方法。

然而,没有什么是完美的。有证据表明,当商店缺少L1时,即使在L2中,订购问题也会导致奇怪的性能问题。


1如果in保持按顺序提交的幻觉,它肯定有可能按顺序提交存储,例如,在恢复顺序之前,不放弃按顺序写入的缓存行的所有权,但这很容易出现死锁和其他复杂情况,我没有证据表明x86会这样做。

最新更新