x86 - L1高速缓存 Controller 的顺序以处理来自CPU的内存请求

标签 x86 hardware cpu-architecture cpu-cache memory-barriers

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

我很好奇:

为了满足从写缓冲区发出的写请求,L1高速缓存控制器是否处理写请求,完成写请求的高速缓存一致性并将数据按与发布顺序相同的顺序插入L1高速缓存中?

最佳答案

您的用语不寻常。您说“完成缓存一致性”;实际发生的情况是,内核必须先拥有(专有)缓存行的所有权,然后才能对其进行修改。在修改发生的瞬间/周期,它成为高速缓存一致性协议中所有参与者共享的内存内容视图的一部分。
因此,是的,您可以“完成缓存一致性” =在商店甚至可以进入缓存并变得全局可见之前获得独占所有权=可用于共享该缓存行的请求。缓存始终保持一致性(这是MESI的重点),而不是保持不同步,然后等待一致性。我认为您的困惑源自您的思维模式与现实不符。
(弱排序的体系结构可能会让人费解,例如,并非所有内核都以相同的顺序从其他两个内核看到存储;这可以由private store-forwarding between SMT threads on one physical core letting another logical core see a store ahead of commit to L1d = global visibility发生。)

我想您知道其中一些内容,但让我从基础开始。
每个核心中的L1缓存参与缓存一致性协议,该协议可使其缓存与一致性域中的其他缓存保持一致(例如L2和L3,以及其他核心中的L1,但GPU内部不包含video-RAM缓存)。
从L1缓存(or from the store buffer或不可缓存的RAM或MMIO)读取数据时,负载在全局范围内可见。 MFENCE可以迫使他们等待更早的存储在全局范围内可见,然后再将L1采样到avoid StoreLoad reordering
当存储将数据提交到L1缓存时,该存储将在全局范围内可见。可能发生的条件是:

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

它是retired来自核心的乱序部分,因此被认为是非推测性的。在退休之前,我们不知道it and all preceding instructions won't fault,也不知道它没有出现分支错误预测或其他错误推测的阴影。
退休只能在完成执行后发生,但与对L1d的承诺无关。存储缓冲区可以继续跟踪非推测性存储,即使ROB(无序执行ReOrder Buffer)忘记了存储指令后,也肯定会最终发生。

先前所有的加载/存储/围栏已经是全局可见的(由于x86的内存排序规则)。这不包括操作较差的操作(NT商店);其他负载/存储可以通过它们。

在当前内核的L1d高速缓存中,高速缓存行处于MESI / MESIF / MOESI高速缓存一致性协议的“排他”或“已修改”状态。如果RFO(所有权读取)在高速缓存的外部级别遇到高速缓存未命中,或者与其他也希望以独占方式访问以写入或原子地RMW的高速缓存行竞争的内核,则可能会花费很长时间。


有关允许的状态转换的图和详细信息,请参见Wikipedia的MESI article。关键点是,在确保没有其他缓存包含该行的情况下,仅允许内核修改其缓存行的副本即可实现一致性,因此不可能存在同一行的两个冲突副本。
英特尔CPU实际上使用MESIF,而AMD CPU实际上使用MOESI允许高速缓存->高速缓存数据传输脏数据,而不是像基本MESI协议所要求的那样写回到共享的外部高速缓存。
另请注意,现代Intel设计(在Skylake-AVX512之前)实现使用large shared inclusive L3 cache as a backstop for cache-coherency,因此探听请求实际上不必广播到所有内核。他们只检查L3标签(其中包含额外的元数据以跟踪哪个内核在缓存什么。
英特尔的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执行退役中解耦。与常规无序窗口大小相比,这可能会隐藏更多的存储延迟。但是,即使有中断到达,退休存储区也必须最终(以正确的顺序)发生,因此允许大量退休但没有提交的存储区会增加中断等待时间。
存储缓冲区尝试尽快将已退休的存储区提交到L1d,但受内存排序规则的限制。 (即其他核心很快就会看到存储;您不需要围墙就可以刷新存储缓冲区,除非您需要当前线程等待该事件发生,然后再加载到该线程中,例如顺序一致的存储。)
在顺序较弱的ISA上,较早的存储仍在等待高速缓存未命中时,以后的存储可以提交到L1d。 (但是您仍然需要一个内存顺序缓冲区,以保留程序顺序中单个核心运行指令的错觉。)
存储缓冲区可能一次有多个未命中的高速缓存未命中,因为即使在顺序严格的x86上,它也可以在该存储区是缓冲区中最旧的存储区之前为该高速缓存行发送RFO。

关于x86 - L1高速缓存 Controller 的顺序以处理来自CPU的内存请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38034701/

相关文章:

cpu - 如果 CPU 一直在执行指令,我们如何衡量它的工作量?

assembly - 在仅读取ZMM寄存器并写入k掩码的512位指令之后,Skylake是否需要vzeroupper来使turbo时钟恢复?

c++ - 包含 MVTec Halcon 库时未声明的标识符

iphone - 如何在运行时确定二进制图像架构?

android - 在android设备中获取制造商名称

c# - 使用依赖注入(inject)进行硬件抽象

x86 - 超线程核心是否共享MMU和TLB?

assembly - 为什么该循环的循环计数在对齐和未对齐的情况下相似?

assembly - 所有asm标签都成为可执行文件中的符号

c++ - 定点硬件光线追踪产品补偿