# 第七章 存储系统

# 7.1 存储系统基本知识

三级存储系统:Cache—— 主存 —— 辅存

四个问题:放置规则,查找算法,替换算法,写策略

# 7.2 Cache 基本知识

S:组,E:路,B:块大小(valid + Tag + 块内容字节数)
Cache 数据容量 = S * E * B
地址字:Tag + 组号 + 组内块偏移量

Tag 和 Data 的并行访问:根据组号确定访问 Cache 的位置后,并行地读取这一组中每个块的 Tag 和 Data,将 Tag 的比较结果传输到 Data 的多路选择器中,选择出目标 Data;
Tag 和 Data 的串行访问:Tag 的比较结果完成后再读取 Tag 符合的缓存块的 Data 值。

如果 Cache 访问时间要求严格(按序处理器),则需要使用并行访问;否则(乱序处理器通过乱序执行指令弥补运行上的延迟)可以串行

“写” 访问有可能导致 Cache 和主存内容的不一致。
写命中时:

  • 写直达法:数据同时写入 Cache 和主存,更加简单可靠,一致性好,但速度较慢
  • 写回法:只写入 Cache,不写入主存,仅当替换时,才把修改过的 Cache 块写回到主存

写缺失时:

  • 写分配法:除了将所要写的字写入主存,还要将包括所写字在内的一个块从主存读入 Cache,常结合写回法
  • 写不分配法:直接写入主存,常结合写直达法

CPU 时间 =(CPU 执行周期数 + 存储器停顿周期数)× 时钟周期时间
=(CPU 执行周期数 + 访存次数 × 缺失率 × 缺失开销)× 时钟周期时间

CPI 较小,固定周期数的 Cache 缺失开销的相对影响就越大;而如果时钟频率越高,损失开销时间较大,Cache 缺失开销的相对影响越大

# 7.3 降低 Cache 缺失率

强制性缺失:冷启动缺失,首次访问缺失;增加块大小,预取解决
容量缺失:当某些块由于 Cache 满被替换;增加容量解决
冲突缺失:某一组中重新访问的冲突;提高相联度解决

相联度越高,冲突缺失就越少;强制性缺失和容量缺失不受相联度的影响;容量缺失随着容量的增加而减少
对于给定的 Cache 容量,当块大小增加时,缺失率开始下降,后来反而上升
原因:虽然减少了强制性缺失,但块数目减少,增加了冲突缺失;增加块大小,加载块需要的开销增大

采用相联度超过 8 的方案实际意义不大;提高相联度会增加命中时间
容量为 N 的直接映象 Cache 的缺失率和容量为 N/2 的两路组相联 Cache 的缺失率差不多相同

伪相联 Cache(Way 预测):一般为 2-way 组相联,保留额外的位来预测一个 Set 中的块,只比较一次 Tag(快速命中),否则检查另一块(慢速命中)
变化的命中时间对 CPU 流水线影响大适合离 CPU 远的 Cache

指令预取:通常由 Cache 之外的硬件完成
或者在编译时加入预取指令,在数据被用到之前发出预取请求;可以预取到寄存器 / Cache 中,故障性(允许预取异常)和非故障性(发生异常放弃操作),也叫非绑定预取
预取应当非阻塞,使执行指令和读取数据重叠执行,主要优化对象是循环

编译器的软件优化:

  1. 程序代码重组:重排序执行过程,将程序入口点与 Cache 块起始位置对齐,分支指令预测优化等
  2. 数据重组:数组合并(提高局部性),内外循环交换,循环融合,分块执行

“牺牲者” Cache:在 Cache 与下一级存储之间设置全相联的小 Cache,用于存放被替换出去的块

# 7.4 减少 Cache 缺失开销

采用多级 Cache(两级),一般第二级 Cache 比第一级大得多,两级 Cache 的全局缺失率与第二级 Cache 相同的单级 Cache 接近
局部缺失率 = 该级 Cache 的缺失次数 / 到达该级 Cache 的访问次数
全局缺失率 = 该级 Cache 的缺失次数 / CPU 发出的访存的总次数
对于 L2,全局缺失率为一二级 Cache 局部缺失率之积
多级 Cache 需要有包容性

在读缺失时,所读单元的最新值有可能还在写缓冲器中,尚未写入主存
处理方式:

  • 推迟对读缺失的处理
  • 检查写缓冲器内容

写缓冲合并:如果写缓冲器中有待写入的数据,就把这次要写入地址与已有地址进行比较,如果有地址匹配而对应的位置空闲的,就把这次要写入的数据与该项合并(同一地址合并,相邻地址的合并成更大的写操作)

关键字处理技术:
关键字:从下一级存储器调入 Cache 的块中,立即需要的一个字;应该尽早把关键字发送给 CPU
尽早重启动:调块时,从块的起始位置开始读起,一旦关键字到达就立即发送,让 CPU 继续执行
关键字优先:从关键字所在的位置读起,立即发送给 CPU
当 Cache 块较小,且下一条指令正好访问这个 Cache 块的另一部分时效果不大

非阻塞 Cache 技术:缺失下命中,缺失下缺失(允许同时处理多个缺失)

# 7.5 减少命中时间

Cache 足够小,可以与 CPU 一起放在同一块芯片上

PIPT 物理 Cache:Tag 和 Index 都是物理地址,地址转换和访问 Cache 串行进行,访问速度很慢
VIVT 虚拟 Cache:Tag 和 Index 都是虚拟地址,命中时不需要地址转换,地址转换和访问 Cache 并行进行
歧义(ambiguity)问题:多进程时相同虚地址,不同物理地址,VIVT 可能存在
别名(alias)问题:不同虚地址,对应相同物理地址,VIVT 可能存在
VIPT:不存在歧义问题,别名问题仍然存在

Cache 访问流水化:并行处理多个缓存访问

踪迹 Cache:缓存指令执行的路径或踪迹,能跨越多个基本块和分支;当处理器预测到某个踪迹时,可以直接从踪迹缓存中取出该踪迹,从而减少指令取指和解码的时间;但机制复杂,可能冗余

# 7.6 并行主存系统

并行主存系统是在一个访存周期内能并行访问多个存储字的存储器,关注的是存储器的带宽
单体单字存储器:一次只能访问一个存储字

单体多字存储器:存储器每个存储周期读出 m 个 CPU 字,最大带宽提高到原来的 m 倍;但是一次读取的 m 个指令 / 数据不一定都是有用的,访存效率不高;且如果读写操作在 m 个存储字内就无法同时完成

多体交叉存储器:多个单字存储体构成,每个体都有自己的地址寄存器以及地址译码和读 / 写驱动等电路
高位交叉编址:同一个体中的高 log2mlog_2m 位相同(按列优先编址)
低位交叉编址:同一个体中的低 log2mlog_2m 位相同(按行优先编址)
在一个存储周期 TMT_M 内,分时启动 m 个存储体,各存储体的启动间隔为 TM/mT_M / m

避免存储体冲突:采用许多体、使存储体个数为素数(硬件),循环交换优化、扩展数组大小(软件)