找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2639

积分

0

好友

379

主题
发表于 9 小时前 | 查看: 0| 回复: 0

了解完CPU缓存的基本概念后,我们就可以深入探讨缓存一致性领域了。

在系统学习缓存一致性之前,了解多核系统的基础知识是必要的。但为了避免割裂整体认知流程,这里我们仅作一个简要说明:多核系统意味着系统内存在多个处理核心,且核心之间需要进行数据交换。数据交互可能发生在共享的L3或SLC缓存中。然而,缓存本身无法被直接寻址,并且某些缓存是相对于核心私有的。因此,在程序运行过程中,我们必须保证在一个时间窗口内,所有核心看到的数据是一致的。

在详细讲解缓存一致性之前,我们先来厘清几个基础概念。

1. 访存空间一致性

设想这样一个场景:

  • a. RAM中同一地址的数据被同时缓存到了Core1和Core2的私有缓存中,此时三者的值是一致的。
  • b. 之后,Core1修改了它缓存中的数据,此时RAM和Core2的数据一致,但和Core1不同。

此时,Core2若继续使用其缓存中的原始数据进行计算,就极有可能导致程序出错,这就是空间上的不一致。

因此,空间一致性指的是在一个时间窗口内,整个系统中对同一内存地址不会存在多个不同值的副本。这个问题通常被称为缓存一致性,而它正是由缓存本身引起的。

2. 访存时间一致性

回看访存空间一致性的描述,我们提到了“时间窗口”的概念。这是因为在多核系统中,硬件通过数据同步机制来实现空间一致性是需要时间的,因此访存的时间一致性问题是天然存在的。

这种因数据同步延迟而导致的一致性问题,被称为访存时序一致性。

3. 理解访存时间一致性问题

为了更清晰地理解访存时间一致性问题,我们不妨先将缓存从系统模型中移除。此时,多核心的所有数据操作都将通过总线网络直接读写SDRAM。我们期望所有核心都能一致地看到其他核心写入SDRAM的数据,但现实情况往往并非如此。

a. 延迟到达导致问题

假设一个系统中有三个核心 Core0、Core1、Core2,并发生以下操作:

  • a. Core0 向地址 A 写入数据 data0。
  • b. Core1 向地址 A 发出读请求,将读到的数据存入寄存器 a,然后向地址 B 写入数据 data1。
  • c. Core2 向地址 B 发起读请求,将数据存入寄存器 a,随后向地址 A 发起读请求,并将数据存入寄存器 b。

由于网络中各个核心之间的物理距离不同或总线仲裁延迟,可能会出现以下场景:Core1 成功读取到了 data0 并完成了向地址 B 的写入,但 Core2 向地址 A 发起的读请求却没有拿到最新的 data0,而是拿到了旧值。

这就会导致 Core0 和 Core1 认为地址 A 的数据是正确的,而 Core2 看到的数据却是错误的。对于地址 B 也存在类似问题。这个问题的根源在于分布式系统中永远无法做到零延迟通信。

b. 访问冲突导致问题

再考虑另一种场景:Core0 和 Core1 同时发起对同一地址的修改操作,例如:

  1. Core0 读取地址 a 的数据到寄存器0,然后对寄存器0进行自增,最后将结果写回地址 a。
  2. Core1 执行完全相同的操作序列。

程序预期的结果是,Core0 和 Core1 的操作具有先后顺序,即在其中一个自增的结果基础上再进行自增。若存在缓存,则需要保证空间一致性;但这里由于网络延迟,必须保证每个核心产生的新数据能够正确同步到其他核心,而同步是需要时间窗口的。因此,当某个核心的新数据尚未被其他核心看到时,其他核心不能操作该数据。这就是由访问冲突或访问延迟所引发的问题。

c. 提前执行导致错乱

我们通过一个代码示例来看这个问题:

core0 core1
代码段 0; 1. 代码段 0
Store data 地址A; 2. load 地址P regA;
Inc 地址P 3. load 地址P1 regB;
代码段 1; 4. cmp regA regB;
5. Load 地址A 寄存器C
6. 代码段 1

Core0 和 Core1 分别执行不同的代码,但存在数据交互。

Core1 在执行时,由于分支预测机制,假设预测条件不跳转,可能会提前执行第 5 行代码(Load 地址A)。如果此时 Core0 尚未完成对地址 A 的数据更新,Core1 就会读到旧数据。然而,在下一次循环执行到指令 1 时,Core1 可能读到了 Core0 更新后的最新数据,从而满足条件跳出循环。

请注意,前一次循环中提前执行的指令 5 读取的却是旧数据,这就会导致程序执行错误。回顾这个问题,其本质是多核之间产生了读后写相关,但核心间的硬件机制未能保证执行顺序而引发的问题。这是计算机基础中并发与内存模型要解决的核心难题之一。

d. 乱序执行导致问题

我们再看一个由乱序执行引发问题的例子:

core0 core1
代码段 0; 1. 代码段 0
Store data0 地址A; 2. Store data1 地址b;
Ioad 地址B regA; 3. load 地址A regA;
代码段 1; 4. 代码段 1;

在这个例子中,Core0 和 Core1 的指令看似相互独立执行,没有交集。但实际上,由于它们操作了相同的内存地址(A 或 B),依然可能像分支预测的例子一样,因为跨核心的读写相关性问题而出错。与分支预测不同的是,分支预测错误可以冲刷流水线重新执行,而乱序执行导致的问题则更加棘手。

因此,最终需要解决的,就变成了如何有效处理跨核心之间的指令相关性问题。本节主要描述了多核系统中访存时间一致性的几种典型问题,下一节我们将探讨当前用于解决这些问题的一些基本方案。




上一篇:基于Claude Code开源桌面AI助手WorkAny,支持文件整理与办公自动化
下一篇:SQL注入新姿势:利用图片EXIF信息绕过防护
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-25 18:20 , Processed in 0.308131 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表