PC_替换算法/cache回写策略
文章目录
常见替换算法
- 替换算法应用在
- cache块-主存块替换
- 主存-外存的页面替换
随机算法RAND
- 随机地确定要被替换的cache块
- 实现简单,但是由于不符合程序局部性原理,可能使得命中率较低
先进先出算法FIFO
- 选择最早调入的cache块进行替换(根据停留在cache中的时间来判断)
- 也比较容易实现,同样不符合程序局部性原理
- 较早进入cache的主存块,及时它非常频繁地被使用,也会由于存在于cache中较长时间而导致被替换
- 可见,仅仅依赖于存留时间是不太理想的替换标准
- 如果能够知道某个块距离上一次访问时间间隔来综合考虑,那么会避免上述的弊端
- 这样,如果某个常用的块在主存中停留了较长时间,但是由于它被频繁访问,因此它的上一次被访问时间点 T l a s t T_{last} Tlast和当下要执行替换操作的时间点 T n o w T_{now} Tnow的间隔不会大,而且应该是比较小的
最近最少使用LRU
- LRU:Least Recently Used
- 这个算法比较符合程序局部性原理
- 算法选择近期一段时间内较长世界未被访问的Cache行替换掉
- 平均命中率比FIFO高,且实现开销不是太大
实现思路
- 采用数值记录主存块的使用情况
- LRU要求对每个cache行设置一个计数器
- 计数器充当一个淘汰权重,的角色,数值越大,被淘汰的优先级越高
- W i W_i Wi:不妨把数值记为 W i W_i Wi,表示第 i i i个cache块的淘汰权重
-
V
i
V_i
Vi:同时,
W
i
W_i
Wi还反映该行最近的被访问情况,不妨用
V
i
=
1
W
i
V_i=\frac{1}{W_i}
Vi=Wi1来描述访问的频繁程度
- V i V_i Vi越大,表示访问第 i i i个cache块也经常被使用
- 根据计数值选择淘汰某个块,计数值的位数和cache组大小有关
- 2路组相联采用1位LRU位
- 4路组相联采用2位LRU位
- 计数器充当一个淘汰权重,的角色,数值越大,被淘汰的优先级越高
LRU计数器变化(维护)规则:
- cpu每次访存,都会影响计数器
-
即,需要对每一行cache行的计数器执行检查/更新操作
-
就算是没有被访问的cache,或者没有命中的访存操作也可能发生计数值的变化
命中时(caseA)
-
🎈SIK操作约定(命中时执行)
-
被命中的行的计数器清零 W i = 0 W_i=0 Wi=0(Set)
- 表示刚刚这些cache行被访问过了
-
比 W j < W i W_j<W_i Wj<Wi小的计数器+1(Increment)
-
其余( W j ⩾ W i W_j\geqslant W_i Wj⩾Wi的)的计数器值保持不变Keep
- 这么约定可以计数器的最大值控制在一定的范围内,同时也不影响算法的有效性
- 理论上这种情况计数器应该是也要+1的(也没有错),但是数值太大不利于表示
- 假设说,cache(分组)的空间足够大,程序很小而且十分符合局部性原理.以至于大量的访问总是可以在分组内完成(命中)
- 再极端一点考虑,cpu执行某个简单循环,总是访问同一条指令,cache组内的其他R-1条已知没有再次被访问,如果不使用keep策略,那么会导致这R-1条的计数器爆增
- 而如果采用keep策略,就可以避免这种爆增情况
- 认真分析可以发现,+1操作能够使得所有计数器的上限值增大的情况,总是发生在未命中的时候🎄)
- 假设说,cache(分组)的空间足够大,程序很小而且十分符合局部性原理.以至于大量的访问总是可以在分组内完成(命中)
-
cache未被命中(caseB)
- 🎈FSI约定操作(未命中时执行):
- 填充置位更新操作(Fill+Set+Increment),简称为FSI操作
- CB1:如果cache中还有空闲空间(空闲cache行)
- 那么将本次访问主存单元所在块(新数据块)调入cache的空闲行
- FSI操作
- CB2:如果cache已满
- 执行LRU淘汰操作,空出某(些)行
- 将新的数据块从主存调入这些新的空行
- FSI操作
-
cache未被命中(另一种描述):
- CB1:如果cache中还有空闲空间(空闲cache行)
- 那么将本次访问主存单元所在块(新数据块)调入cache的空闲行
- CB2:如果cache已满
- 执行LRU淘汰操作,空出某(些)行
- 将新的数据块从主存调入这些新的空行
- 🎈CB1,CB2两类情况均要以FSI操作收尾
- CB1:如果cache中还有空闲空间(空闲cache行)
-
特点:
-
当(映射到同一个cache分组的)集中访问的(主存)存储区域(或者说区域容量)超过了cache组的大小
-
命中率可能会变得很低
-
这种现象称为抖动
-
例🎈
-
假定采用4路组相联,用5个不同的主存块{ B 1 , B 2 , B 3 , B 4 , B 5 B_1,B_2,B_3,B_4,B_5 B1,B2,B3,B4,B5}映射到cache的同一个组
-
上述的5个块在主存中可能使任意的顺序,且未必是相邻的
-
将5个块简写为{1,2,3,4,5}
-
假设主存访问序列是1,2,3,4,1,2,5,1,2,…
-
cache分组的4个cache行\访问的主存块序列 1 2 3 4 1 2 5 1 2 0 0 1 1 1 2 1 3 1 0 1 1 1 2 1 0 1 1 1 1 0 2 1 2 2 2 3 2 0 2 1 2 2 2 0 2 2 0 3 1 3 2 3 3 3 0 5 1 5 2 5 3 0 4 1 4 2 4 3 4 3 4 3 4 -
每一大列
- 左分列表示该cache行的计数器值( W i W_i Wi)
- 右分列表示该cache行调入(持有)的主存块(块号 B x B_x Bx)
- 本例中, x = 1 , 2 , 3 , 4 , 5 ; i = 0 , 1 , 2 , 3 x=1,2,3,4,5;i=0,1,2,3 x=1,2,3,4,5;i=0,1,2,3
-
最不经常使用算法LFU
- LFU:Least Frequently Used
- 和LRU类似
- 将一段时间内被访问次数最少的存储行换出
- 每一行cache行设置一个被访问次数计数器
- 新行建立后从0开始计数
- 每被访问一次,被访问的行计数器+1
- 在需要替换时比较各特定行的计数值,将计数值最小的
Cache写策略
- 因为Cache中的内容是主存块副本,当对Cache中的内容进行更新时,就需选用写操作策略使Cache内容和主存内容保持一致.
- 此时分两种情况(双向)
- cache写入主存
- 主存调入(写入)cache
Cache 写命中( write hit)
全写法
- 全写法(也叫写直通法,write-through).
- 当CPU对Cache 写命中时,必须把数据同时写入Cache 和主存.
- 当某一块需要替换时(不命中),不必把这一块写回主存,用新调入的块直接覆盖即可.
- 优点:
- 这种方法实现简单,能随时保持主存数据的正确性.
- 缺点
- 是增加了访存次数,降低了Cache的效率.
写缓冲
- 为减少全写法直接写入主存的时间损耗,在Cache和主存之间加一个写缓冲(Write Buffer ),
- CPU同时写数据到Cache 和写缓冲中,写缓冲再将内容写入主存.
- 写缓冲是一个FIFO 队列,写缓冲可以解决速度不匹配的问题.
- 但若出现频繁写时,会使写缓冲饱和溢出.
回写法
- 回写法( write-back).
- 当CPU对 Cache 写命中时,只把数据写入Cache,而不立即写入主存,只有当此块被换出时才写回主存.这种方法减少了访存次数,但存在不一致的隐患.
- 为了减少写回主存的开销,每个Cache 行设置一个修改位(脏位).
- 若修改位为1,则说明对应Cache行中的块被修改过,替换时需要写回主存;
- 若修改位为0,则说明对应 Cache行中的块未被修改过,替换时无须写回主存.
小结
- 全写法和回写法都对应于Cache写命中(要被修改的单元在Cache中)时的情况.
Cache 写不命中
写分配法
- 写分配法 ( write-allocate).
- 加载主存中的块到Cache 中,然后更新这个Cache块.
- 它试图利用程序的空间局部性,但缺点是每次不命中都需要从主存中读取一块.
非写分配法
- 非写分配法( not-write-allocate)
- 只写入主存,不进行调块.
小结
- 非写分配法通常与全写法合用,写分配法通常和回写法合用.
分离的Cache结构
- 随着新技术的发展(如指令预取),需要将指令Cache和数据Cache分开设计,这就有了分离的Cache结构.
- 统一Cache 的优点是设计和实现相对简单,但由于执行部件存取数据时,指令预取部件要从同–Cache读指令,会引发冲突.
- 采用分离Cache 结构可以解决这个问题
- 而且分离的指令和数据Cache还可以充分利用指令和数据的不同局部性来优化性能.
- 现代计算机的Cache 通常设立多级Cache,假定设3级Cache,按离CPU的远近可各自命名为Ll Cache、L2 Cache、L3 Cache,离CPU越远,访问速度越慢,容量越大.
- 指令Cache与数据Cache分离一般在L1级,此时通常为写分配法与回写法合用.
- 例如
- L1 Cache对 L2 Cache使用全写法,
- L2 Cache对主存使用回写法,
- 由于L2 Cache的存在,其访问速度大于主存,因此避免了因频繁写时造成的写缓冲饱和溢出.
- 指令Cache与数据Cache分离一般在L1级,此时通常为写分配法与回写法合用.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库