“在计算机里,CPU 运算速度快得惊人,每秒能处理数十亿次运算,就像一辆飞速行驶的跑车。但主存传输数据的速度却很慢,好比在拥挤街道艰难前行的车辆。CPU 与主存速度的巨大差距,严重阻碍了计算机高效运行,拖了系统性能的后腿。”
Cache 的出现解决了这个问题。它利用两个特性来工作:
一是时间相关性( temporal locality),如果一个数据现在被访问了,那么在以后很有可能还会被访问。--- 比如你做视频剪辑时反复用的视频片段,它知道这些数据很可能还会被使用;
二是空间相关性( spatial locality),如果一个数据现在被访问了,那么它周围的数据在以后有可能也会被访问。 --- 像视频片段周围相邻的数据,也容易被调用。
基于这两点,Cache 提前把 CPU 可能要用的数据存好,等 CPU 要数据时,能马上给到,让计算机运行得更快更流畅 。
一、Cache 是什么
Cache 是一种高速缓冲存储器,位于 CPU 和主存之间。其存在的主要目的是解决 CPU 运算速度与主存读写速度不匹配的问题。
程序运行于主存(如 DDR 内存),当 CPU 执行进程时,从磁盘将可执行程序加载到主存。以 CPU 对变量地址 A 加 1 为例,需从主存读取数据到通用寄存器、寄存器运算+1后再写回主存。
CPU 寄存器速度极快(小于 1ns),主存却很慢(约 65ns),导致数据读写操作耗时久。
因提升大容量主存速度成本过高,于是采用折中的办法,在 CPU 和主存间增设一块速度极快但容量极小的存储设备 ——cache。
cache 作为主存数据缓存,CPU 读写数据时先查 cache,若有则直接获取,将 CPU 与主存间直接数据传输转变为 CPU 与 cache 间直接传输,由 cache 负责与主存的数据交互 。
二、Cache 的工作原理
(一)命中与失效率
-
命中:当 CPU 需要访问数据时,首先会在 Cache 中查找。如果能在 Cache 中找到所需数据,这被称为命中。
“CPU这样判断是否命中当CPU需要访问数据时,首先会根据内存地址通过特定的地址映射机制,将内存地址转换为Cache中的地址。常见的地址映射方式有直接映射、全相联映射和组相联映射等。例如在直接映射中,内存地址的某些位被用来直接确定其在Cache中的存储位置。 找到对应的Cache位置后,CPU会将该位置的标记字段与要访问的内存地址的标记部分进行精确比较。标记字段是用于唯一标识该Cache块所对应的主存区域的信息。同时,CPU还会检查该Cache块的有效位,有效位用于表明该Cache块中的数据是否为有效数据。只有当标记字段匹配且有效位为真时,才能确定命中Cache,此时CPU可以从Cache中快速读取数据。 下面通过一个例子来帮助理解:假设Cache就像一个有很多小格子的柜子,每个小格子可以放一些数据。内存地址就像是一个大仓库里货物的编号。当CPU要找某个货物(数据)时,它会先根据货物编号的一部分,比如编号的最后几位数字,来确定这个货物可能在柜子的哪个小格子里(通过地址映射找到Cache位置)。然后看看这个小格子上贴的标签(标记字段),是不是和货物编号的其他部分能对应上,同时还要看看这个小格子里的货物是不是完好可用的(检查有效位)。如果标签对应上了,货物也是好的,那就说明在这个小格子里找到了要的货物,也就是命中了Cache。”
-
失效率:反之,如果在 Cache 中未找到所需数据,就发生了失效,需要从主存中读取数据。失效率为 1 减去命中率,即失效率 = 1 - 命中率。失效率越低,说明 Cache 的效果越好。
(二)数据加载与替换
-
Cache line:Cache 与主存之间的数据交换以 Cache line 为基本单位。Cache line 是一块连续的存储单元,通常大小为 32 字节、64 字节或 128 字节等。当 CPU 访问的数据不在 Cache 中时,会将包含该数据的整个 Cache line 从主存加载到 Cache 中。例如,假设 Cache line 大小为 64 字节,CPU 需要访问的一个字节数据不在 Cache 中,那么主存中从该字节开始的连续 64 字节数据会被加载到 Cache 中。
-
替换策略:当 Cache 已满,需要加载新的 Cache line 时,就需要一种替换策略来决定淘汰 Cache 中的哪些数据。常见的替换策略有:
-
最近最少使用(LRU):该策略认为最近最少被使用的数据在未来被使用的概率也较低。因此,当需要替换数据时,会淘汰 Cache 中最近最少使用的 Cache line。例如,Cache 中有 A、B、C 三个 Cache line,其中 A 最近被访问过,B 其次,C 最长时间未被访问,那么当需要替换时,C 会被淘汰。
-
先进先出(FIFO):按照数据进入 Cache 的先后顺序进行替换,先进入 Cache 的数据先被淘汰。比如,先进入的 Cache line 为 D,随后是 E 和 F,当需要替换时,D 会被淘汰。
-
随机(Random):随机选择一个 Cache line 进行替换。虽然这种策略简单,但缺乏对数据访问模式的考虑,性能相对较差。
三、Cache 的写策略
当 CPU 对 Cache 中的数据进行写操作时,有两种常见的写策略:
-
写直达(Write - Through):在对 Cache 进行写操作的同时,立即将数据写入主存。这种策略的优点是数据一致性好,因为主存中的数据始终与 Cache 中的数据保持同步。但缺点是每次写操作都需要访问主存,速度较慢,会增加系统的写延迟。
-
写回(Write - Back):先将数据写入 Cache,只有当该数据所在的 Cache line 被替换时,才将其写回主存。这种策略减少了对主存的写操作次数,提高了写性能。但在数据一致性方面相对较弱,因为主存中的数据可能不是最新的。为了解决这个问题,通常会在 Cache 中设置一个脏位(Dirty Bit),用于标记 Cache line 中的数据是否被修改过。当 Cache line 被替换时,如果脏位为 1,则将数据写回主存。
上面的例子中,内核修改了x,当时x并没有立刻更新到内存中。
Cache怎么知道这行的数据有没有被修改呢?这就需要新增一个标志--dirty标志。dirty标志为1,表示 Cache的内容被修改,和内存的内容不一致,当该Cache line被移除时,数据需要被更新到内存, dirty标志位为0(称为 clean),表示Cache的内容和内存的内容一致。
当CPU执行store指令并在cache命中时,我们只更新cache中的数据。我们会将dirty bit置位。主存中的数据只会在cache line被替换或者显示的clean操作时更新。