0x1 cpu缓存体系结构
为什么需要缓存
现代cpu的运行速度远远超过了内存的访问速度,一次的内存访问与一次的cpu访问是2个数量级的区别,最终执行每条指令都需要等待内存加载,导致cpu流水线停顿。因此现代cpu在内存之上加了多级的高速缓存,缓解由于不对称带来的性能损耗。
缓存如何解决问题
缓存利用了程序的局部性原理,使得大多数的访问都能维持在高速缓存。
- 空间局部性:数据和代码的访问一般访问了其中一个元素,相邻的下一步就会访问到
- 数据段:结构体,数组,栈
- 代码段:顺序执行
- 时间局部性:相同的信息在不久的将来会再次访问,譬如循环的代码。
0x2 缓存如何工作
缓存的构造
下图是一个32位的,具有16个Set,2个way,每个缓存行256字节的高速缓存。
- Tag:tag的位数由Set的个数和data的大小决定,
tag = 32 - log2(sets) - log2(data) = 20
缓存读取流程

| 缓存行定位策略 | 优点 | 缺点 |
|---|---|---|
| tag/index为虚拟地址 | 访问无需经过页表的转换 | 1. 进程切换会导致缓存失效 1. 共享的内存需要保存多份 |
| tag/index为物理地址 | 与虚拟地址相反 | |
| tag为物理地址,inde为虚拟地址 (虚拟地址和物理地址具有相同的页内偏移,而且比高速缓存的index+offset大) |
地址转换和高速缓存读取可以并行提高读取效率 |
缓存写入流程
- 缓存存在的回写策略
- write-back:只写缓存,只有缓存被淘汰的时候才需要写入内存
- write_through:同时写缓存和内存
- 缓存失效的写入策略
- 写分配:分配缓存,将所需要写的数据写入缓存
- 不按写分配:直接写入内存 | 回写策略 | 分配策略 | 当……时 | 写到…… | | —- | —- | —- | —- | | 写回 | 分配 | 命中 | 缓存 | | 写回 | 分配 | 失效 | 缓存 | | 写回 | 非分配 | 命中 | 缓存 | | 写回 | 非分配 | 失效 | 内存 | | 写通 | 分配 | 命中 | 快取和内存 | | 写通 | 分配 | 失效 | 快取和内存 | | 写通 | 非分配 | 命中 | 快取和内存 | | 写通 | 非分配 | 失效 | 内存 |
0x3 缓存一致性协议MESI

- M(modified):已修改,这个时候缓存和主存是不一致的。
- E(Exclusive):独占,这个时候缓存和主存是一致,并且只有当前这个cpu有这个缓存。可以直接修改缓存。
- S(shared):共享,这个时候缓存和主存是一致的,有多个cpu都拥有该缓存,如果要修改缓存需要先广播一个消息让其他cpu置为无效,收到回复后再修改。
- I(invalidated):已失效。
状态机
存储缓存区
- Q:cpu在写入一个数据到缓存的时候,需要先获取缓存的读写权限,这个时候需要等待其他cpu的回复。
- A:在缓存和cpu之间增加store-buffer,先缓存要写入的数据,之后等到其他cpu回复再刷入缓存。
无效队列



