编写缓存友好代码

  • 上篇文章讲了高速缓存,高速缓存位于CPU内部,处理器进出的数据都要先经过高速缓存,不命中的再到外边处理,即高速缓存会拦截处理器全部请求。
  • 值得注意的是,高速缓存是透明的,即虽然有这样机制存在,但是上层应用程序完全感受不到缓存的拦截、处理操作
  • cash结构以二维阵列形式存储,包含SEB三个参数,S是组的数量,E每组块的数量,B是每块储存的字节。S/E/B都是2的整数次幂
  • 每一块包括v,b部分,这里要注意v不算在B里边。映射的基本思路是将处理器给出的地址划分成三段。b块内偏移量,s组索引,t与tag做匹配,再根据请求指令的数据宽度读数据
缓存类型 内容 优势 劣势
直接映射高速缓存 E=1,每组只有一个块 结构简单,实现速度快 组中只有一个缓存块,易产生冲突不命中,导致缓存性能下降
组相连高速缓存 E>1,每组包含多个块,是最常用的电路缓存结构,又称为E路组相连高速缓存 兼顾了灵活性和实现速度
全相连高速缓存 S=1,只有一组。只要把地址分为块偏移量和tag两部分,不需要组索引 灵活性高,数据可以缓存到任意指令块 结构复杂,实现速度慢

写策略

高速缓存的读操作简单,不涉及数据变化;而写策略会导致数据出现不一致问题,为何?

在存储层次中,一份数据有可能在不同层次上有多份拷贝。

从命中和未命中两种情况讨论写策略

命中包括直写和回写
  • 直写:一次性把所有路径上要修改的数据全修改掉。是从悲观角度考虑,即认为只要有数据不一致的情况,就可能导致出现错误。如插U盘没有弹出直接拔出,编写文档断电再次开机出现恢复文档,就是由于没有采用直写策略。但是这种策略要将数据全部修改,因此只在可靠性要求较高的情况下使用直写操作,从而避免某个时间段出现数据不一致的情况
  • 回写:命中后只把命中的缓存部分改掉,下边的暂且不关心;当修改的数据块被替换时,再向下一级缓存替换数据。使用dirty标志位表示数据在缓存过程中是否被修改过。当此缓存块被替换时,检查是否被置位,若被置位则更新到下一级缓存中,以此类推,一次次更新。回写方式效率高,但是短时间内数据可能不一致。在系统断电或特殊情况可能导致数据差异,即本来应该更新但是却没变。处理器中高速缓存主要采取回写策略,目的是提高性能
写不命中写分配和写非分配
  • 写分配:写入数据时,写入数据不在缓存中,则先加入缓存中。即写分配会导致缓存的更新
  • 写非分配:未命中就一次修改到内存中,直接改,不会导致缓存更新
总体
  • 缓存的写策略理论上有四种,但是真正使用中只用两种。直写策略和非写分配策略都是悲观策略,都要更新到最底层,一般捆绑使用;写分配和回写是乐观策略,注重效率,一般捆绑使用。高速缓存主要采取回写+写分配

缓存的进一步理解

  • Intel处理器的i7高速缓存处理器有四个核,每个核都有寄存器,寄存器下边是一级缓存(包括数据缓存和指令缓存),一级缓存中指令和数据是分开存储的。值得注意的是,在冯诺依曼体系结构中数据和指令同时存储,比较简单;而哈佛结构中指令和体系分开存储,有分别的处理器。因此我们可以看到,现代计算机中,哈弗结构不是消失了,而是和冯诺依曼结构做了进一步融合。即微观上是哈佛结构,把指令和数据分开存储了。一级缓存下是二级缓存,多个二级缓存共用一个三级缓存,然后才是内存
缓存对性能十分重要,那么我们如何评价缓存性能呢?
  • 缓存性能主要由未命中率、命中时间和未命中惩罚三者决定。
  • 未命中率是统计学上的概念,一级高速缓存容量较小,未命中率为3~10%偏高;二级高速缓存容量变大,未命中率小于1%。通常,缓存容量越大,未命中率越低,但是成本越高
  • 命中时间:访问高速缓存命中时,从里边取数据需要的时间。一级缓存:1~2时钟周期;二级:5~20个时钟周期
  • 未命中惩罚:访问缓存未命中时,需要花费多少时间从下层缓存取数据,即缓存未命中需要的额外时间。假设计算机只有一级高速缓存,此时未命中会直接访问内存(50~200个时钟周期)
  • 如何计算缓存系统性能:通过平均访问时间来看=命中时间乘命中率+未命中惩罚乘未命中率。由于一级缓存的性能大约是内存的100倍,未命中惩罚太高,主要由命中率决定。(极小的命中率差异就能导致极大的性能差距)编写缓存友好代码_第1张图片

你可能感兴趣的:(计算机基础)