当前位置: 首页 > 知识库问答 >
问题:

L1 高速缓存控制器的顺序,以处理来自 CPU 的内存请求

公良向阳
2023-03-14

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

我很好奇:

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

共有2个答案

谢英光
2023-03-14

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

但是,您在注释中提到,您担心这可能会影响性能,因为这实际上是让存储缓冲区提交一个阻塞(序列化)进程:

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

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

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

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

尽管如此,没有什么是完美的。有证据表明,当商店错过L1时,订购问题会导致奇怪的性能中断,即使它们在L2上市。

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

谭向晨
2023-03-14

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

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

(弱排序架构具有令人费解的可能性,例如并非所有内核都以相同的顺序查看来自其他两个内核的存储,这可以通过一个物理内核上的SMT线程之间的私有存储转发来实现,从而使另一个逻辑内核在提交到L1d =全局可见性之前看到存储。

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

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

当从L1缓存(或存储缓冲区或不可缓存RAM或MMIO)读取数据时,负载变得全局可见。MFENCE可以强制它们在采样L1之前等待早期存储变得全局可见,以避免StoreLoad重新排序。

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

>

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

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

    退役只能在完成执行后发生,但与对L1d的promise无关。存储缓冲区可以继续跟踪最终肯定会发生的非推测存储,即使在ROB(乱序执行重排序缓冲区)忘记存储指令之后。

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

    在当前内核的L1d缓存中,缓存行处于MESI/MESIF/MOESI缓存一致性协议的独占或修改状态。如果RFO(所有权读取)在外层缓存中遇到缓存未命中,或者与其他内核发生争用,而这些内核也希望以独占方式写入或自动RMW缓存行,这可能需要很长时间。

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

    英特尔CPU实际上使用MESIF,而AMD CPU实际上使用MOESI来允许缓存-

    另请注意,现代英特尔设计(在Skylake-AVX512之前)实施使用大型共享内含L3高速缓存作为高速缓存一致性的后盾,因此监听请求实际上不必广播到所有内核;他们只检查L3标签(包含额外的元数据来跟踪哪个内核缓存什么。< br >英特尔的三级缓存具有标记包容性,即使内部缓存处于独占或修改状态的行在三级缓存中也是无效的。请参阅本文,了解英特尔工作的简化版本的更多详细信息)。

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

    是的,商店按照程序顺序致力于L1,因为这是x86要求它们变得全球可见的顺序。L1提交顺序与全局可见性顺序是一回事。

    您应该说“获取缓存行的所有权”,而不是“完成缓存一致性”。这涉及使用缓存一致性协议与其他缓存进行通信,因此我猜您的意思可能是“使用缓存一致协议完成独占所有权”。

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

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

    存储缓冲区尝试尽快将退役的存储提交到L1d,但它受到内存排序规则的限制。(即,其他内核将很快看到存储;您不需要Geofence来刷新存储缓冲区,除非您需要当前线程在此线程中稍后加载之前等待它发生。例如,对于顺序一致的存储。)

    在弱排序的 ISA 上,较晚的存储可以提交到 L1d,而较早的存储仍在等待缓存未命中。(但是您仍然需要一个内存顺序缓冲区来保留单个内核按程序顺序运行指令的错觉。

    存储缓冲区可以一次有多个高速缓存未命中,因为即使在强有序x86上,它也可以在该存储是缓冲区中最早的存储之前为高速缓存行发送RFO。

  •  类似资料:
    • 现在的CPU比25年前要精密得多了。在那个年代,CPU的频率与内存总线的频率基本在同一层面上。内存的访问速度仅比寄存器慢那么一点点。但是,这一局 面在上世纪90年代被打破了。CPU的频率大大提升,但内存总线的频率与内存芯片的性能却没有得到成比例的提升。并不是因为造不出更快的内存,只是因为太 贵了。内存如果要达到目前CPU那样的速度,那么它的造价恐怕要贵上好几个数量级。 如果有两个选项让你选择,一个

    • Adobe Bridge 可处理并维护所有音频和视频回放文件的高速缓存。这项功能可以提升回放文件的性能,因为只要您以后想查看这些文件,就可以随时访问。建议您定期清理陈旧和不使用的媒体高速缓存文件,以便优化性能。在删除了高速缓存文件后,如果源媒体需要,则随时可以重新生成相应的高速缓存文件。 设置媒体高速缓存首选项 可执行以下步骤来设置媒体高速缓存首选项: 执行以下操作之一: (Windows) 选择

    • 问题内容: 在linux系统中,pthreads库为我们提供了用于对齐缓存的功能(posix_memalign),以防止错误共享。要选择架构的特定NUMA节点,我们可以使用libnuma库。我想要的是同时需要两者的东西。我将某些线程绑定到某些处理器,并且我想为来自相应NUMA节点的每个线程分配本地数据结构,以减少线程的内存操作延迟。我怎样才能做到这一点? 问题答案: 如果您只是希望围绕NUMA分配

    • 使用 rax-plugin-pwa 插件,可以方便快捷的使用 Service Worker 控制缓存,以获得更快的加载速度。 首先,安装 build-plugin-rax-pwa 插件依赖: $ npm install build-plugin-rax-pwa --save 在工程配置 build.json 中添加 pwa 插件并配置缓存目标: { "plugins": [ [

    • 问题内容: 我有3个相互依赖的xsd文件来构建我的元素定义。每个xsd文件都有其自己的名称空间。当我使用JAXB xjc生成类时,得到3个相应的包。到目前为止,一切都很好。 当我想使用解组器进行架构验证时,就会出现我的问题。为了避免不得不读取xsd文件,我从被解组的相关类中动态生成了模式。但是,由于该类依赖于其他2个包中的对象,因此除非我指定所有3个包,否则它无法生成架构。这已经不是一个非常实用的

    • 页面是动态生成的,因为现在我需要,所以不能缓存整个页面。但至少我想缓存所有静态内容,包括页面中包含的图像,有两种方式: 以及以常规的方式 在Varnish配置文件中,图像如和url被设置为缓存。 当页面在浏览器中打开两次,清漆似乎工作正常,和年龄是 现在,当我运行一个脚本来预热清漆缓存,然后第一次在浏览器中打开任何页面时,所有的jpgs、pngs和image.php年龄总是=0 用于预热缓存的脚本