文章目录
引语
在上篇和中篇里,我们讲述了 CPU Cache 相关的基础知识以及映射方式和行替换策略,本文中我们将对 Cache 的写策略和 CPU 的缓存一致性进行介绍
上篇:CPU Cache 知识详解之基础概念与映射方式(上)
中篇:CPU Cache 知识详解之Cache_Miss与替换策略(中)
笔者能力有限,对硬件方面知识并不熟悉,以及本文知识过于底层,网上资料也不太能清晰介绍,此文能提供的帮助十分有限,仅作为个人学习的一点笔记
读策略简单介绍
读策略我们没有独立地介绍,但是在前文中我们介绍了一个内存地址如何映射到高速缓存中以及缓存不命中时的行替换策略,这两者就构成了高速缓存的读策略,当 CPU 需要读取一个数据时,就去高速缓存中读取,如果高速缓存中不存在就会去下一层内存中读取,如果存在就发生行替换,反之继续向下层查找。
写策略
CPU Cache 中的写策略需要分两种情况,一种为写的数据已经在缓存中,即写命中(write hit),另一种就是写不命中。另外在学习写策略时,我们最好先不考虑缓存一致性,或者假设是单核 CPU
写命中(Hit)
write-through(直写/写穿透)
就是将数据写入缓存,同时立即写入主存中,但是直写的缺点是每次写都会引起总线流量(此话是《深入理解计算机系统》中原话,我没有太多硬件相关知识,不能很好地理解,但是大致意思应该就是性能低)。
write-back(写回)
这种方式不会立即将数据写入到主存中,修改会在缓存中堆积一段时间,直到该数据块将被驱逐出缓存时才写入到主存中,这种方式可以减少总线流量,但是增加了复杂性,需要另外维护一个修改位
写不命中(Miss)
write-allocate(写分配)
相当于出现了读不命中,加上写命中策略(read miss + write-through / write-back),写分配试图利用写的空间局部性(书中原话,个人理解:对未命中的块进行加载了,相当于赌下一个 CPU 会写此时修改的数据它周围的数据)
通常情况下都是写回高速缓存通常是写分配的
缺点可能会出现在向下一层加载数据块时会一直出现 Miss 直到主存的情况,效率相对低
no-write-allocate(非写分配)
跳过高速缓存,直接写入主存中
通常情况下都是直写高速缓存通常是非写分配的
缓存一致性
缓存一致性指的是在多个处理器对同一个内存块同时进行读写操作时引发的冲突和数据不一致的问题;以上内容都是在单核处理器上的介绍,存在一个 CPU 时自然没有缓存一致性
解决 CPU 一致性的最直接的方法就是直接所有 CPU 核心都使用一个缓存,但是这样的会导致有些 CPU 核心所需要的数据和指令没法存入缓存中,导致无法进行运算
CPU 之间需要一些协议来保证缓存的一致性,这类协议有 MSI 和 MESI 等(个人只学习过 MESI 的一些皮毛)
MESI
MESI 通过给每一个 Cache Line 设置一个大小为 2bit (四个状态)的状态位来保证一致性
状态 | 描述 |
---|---|
M(Modified) | 被修改的,该 Cache Line 被当前 CPU 核心修改了,和主存不一致,但是是最新的数据,可以直接使用 |
E(Exclusive) | 独占的,该 Cache Line 只有当前 CPU 核心有,而且数据和主存一样,可以直接使用 |
S(Shared) | 共享的,有多个 CPU 核心里有该 Cache Line,而且数据和主存一样,可以直接使用 |
I(Invalid) | 失效的,表示该 Cache Line 无效,需要读取数据就直接取主存中读取即可 |
例子
情景:假设现在存在一个 CPU 存在两个核心 core a, core b;
读取数据
core a 需要读取数据 x
core a 会先查看自己的缓存中是否有该数据:
- 如果存在,就查看 Cache Line 的状态,如果是 M,E,S 的话直接使用即可
- 如果不存在,或者 状态是 I ,就会向总线发起读请求,其他 CPU 核心就会监听该消息,并检查自己有没有该数据;
- 如果 core b 缓存中有该数据, 状态是 E ,说明 core b 的缓存和主存一致,那么 core b 就会把该 数据发送到总线,让 core a 去获取数据,还需要把状态改为 S
- 如果 core b 缓存中有该数据,状态是 S ,说明 core b 的缓存和主存一致,那么 core b 就会把该 数据发送到总线,让 core a 去获取数据
- 如果 core b 缓存中有该数据,状态是 M,说明 core b 的缓存和主存不一致,那么 core b 需要先把数据写到主存中,把状态改为 S,分享给 core a
- 如果 core b 缓存中有该数据,状态是 I,core a 就直接到主存中读取数据即可
写入数据
core a 需要写入数据 x
core a 会先查看自己的缓存中是否有该数据:
- 如果存在,并且 Cache Line 的状态为 M ,说明该数据只有在 core a 中是最新的,那么直接修改数据即可
- 如果存在,并且 Cache Line 的状态为 E ,说明该数据只有在 core a 中是最新的,那么直接修改数据,然后修改状态为 M
- 如果存在,并且 Cache Line 的状态为 S ,说明该数据在其他核心中存在备份,那么需要发消息到总线中,通知到其他的 CPU 核心, 自己对该数据进行了修改, 其他核心需要把自己对应的 Cache Line 的状态改为 I ,即无效,core a 需要修改状态为 M
- 如果不存在或者为 Cache Line 状态为 I ,就需要先读取数据到缓存中(回到上面的读取数据流程),然后修改状态为 M ,同时发消息到总线通知其他核心将该数据状态改为 I
后记
到此关于 CPU Cache 相关知识的上中下篇就结束了,笔者能力有限,文章可能存在许多错误,望读者指出,谢谢