计算机组成原理:6、高速缓冲存储器

Posted by 蔡华的博客 on October 3, 2018

概述

为何使用Cache

  • CPU速度太快了
  • 解决CPU空等问题,但是需要保证的是大多数的数据和指令都在cache中。
程序访问局部性原理
  • 时间局部性:当前正在使用的指令和数据,在不久的将来还会被使用。那么这些数据和指令需要放到cache。
  • 空间局部性:当前正在使用的指令或者数据的相邻的指令或者数据在不久的将来会被使用,那么把当前使用的和相邻的数据和指令放到cache。

Cache工作原理

主存和缓存的编址

主存和缓存的编址

  • 图中主存和cache中块的大小一样,主存分成M个块、cache分成C个,显然C远小于M。
  • 标记:主存块和cache块之间的对应关系,如果一个主存块被调入cache,那么主存块号写入到标记中。当CPU想要访问主存时,根据主存块号先去cache中与标记对比。如果找到,那么说明想要在内存中获取的数据已经在cache中了。
  • 注意:cache-主存这个结构中是按块进行存储和传输的。块的大小和块内地址(本质是偏移量)是完全相同的。在有标记的情况下cache是不需要特别编址的。
命中与未命中
  • 定义:CPU使用主存块时,如果块已经调入缓存则命中;如果没有则是未命中。如果命中那么主存中某些块与缓冲块建立了对于关系(标记)。
Cache的命中率
  • CPU欲访问的信息在Cache中的比率
  • 简单来说和cache的容量和块长有关
  • 一般每块取4-8个字,块长取一个周期内从主存调出的信息长,与前面的低位交叉相关。如果是4体交叉,块长就是4个存储字长。
Cache-主存系统的效率

Cache-主存系统的效率

Cache的基本机构

Cache基本结构

  • 上图的流程中其实包含了cache读取的流程。
  • 最下面主存和CPU之间是有数据总线的,当cache未命中时主存先把数据送入CPU,同时与cache进行数据交换。
  • 地址映射:给出一个规则,主存当中的块如果要放入cache,它可以放入哪一个或哪几个块中。
  • 地址变换:主存块号或地址转换成cache块号或地址。
  • 替换结构:通过替换算法来决定哪些块从cache退出
  • 注意:图中可装进的判断不是以整个cache为基础的,而是以地址映射的结果为基础。e.g: 未命中发生,经过映射计算后主存的块要进入add1,此时如果add1已经有数据了,就要替换;但是此时cache可能并不满,而只是add1有数据。而且这个还要和具体的映射方式有关,见下面内容。

Cache的读写操作

读操作
  • 参考上图,因为读操作的特点是不会改变主存和cache的内容,所以相对比较简单。
写操作
  • 是否实时同步cache和主存就是写操作要关注点了。有两个方法:
  • 写直达法(Write-through):
    • 写操作时数据既写入cache也写入主存
    • 写操作时间就是访问主存时间
    • cache退出块时不需要对主存进行写操作,更新策略比较简单。
  • 写回法(Write-back)
    • 写操作只把数据写入cache而不写入主存
    • 当cache数据被替换出去时才学会主存
  • 比较:
    • 显然写操作从逻辑上简单,但是操作步骤太多。尤其是做循环加法这样的操作时,我们只关注结果,中间数值没必要写入主存。这点上写回法就好很多,它只写最后的结果。
    • 但是写回法在多处理器时就会出现数据一致性问题,并发计算时主存块在多个CPU cache中都有副本,很难保证同步。

Cache的改进

  • 增加cache的级数:
    • 离CPU比较近或者离CPU中的core比较近的cache直接做入到CPU内部(片载cache),CPU内部接着做多级。现代多核CPU每个core都有自己的cache,core之间还有共享的cache,所以至少都有三级的cache。
    • 主板上做大容量cache,片外cache。
  • 统一缓存和分立缓存:指令和数据分开放(分立缓存)

Cache-主存的地址映射

直接映射

直接映射

  • 对主存分区,每个区的第0块就放到cache的第0块。从图中可以看到cache的0块可能是任何一个区的第0块。
  • 主存区号就是cache的标记,因为只要区号和标记一致,那么主存区中的块和cache的块就能一一对应,同样字块内地址也是一一对应的。
  • 弊端:对于cache的使用有浪费,比如一个区只有一个块被用到了,但是如果出现代码的调转等操作导致需要加载新的cache,只能整块的替换。

全相联映射

全相联映射

  • 主存中任何一个块可以放入cache中任何一个块。
  • 弊端:因为是随便放的,所以CPU使用数据时要把主存地址和cache标记一一对比,导致效率低,线路也会比较复杂。同时cache标记记录的是区号+块号,位数比较多,比较器长度比较长。

组相联映射

组相联

  • cache分成多少组,主存储器中每个区就包含了多少块。
  • 主存在往cache放块的时候,只要计算出这个块是该区的第i个,那么就会放入到cache中第i组中。
  • 主存的块只能放到指定的组(体现了直接相连),但是这个主存块可以在组的任何位置(体现了全相连)。

总结

  • 直接映射:某一主存块只能固定映射到某一缓冲块,速度快但是不灵活。
  • 全相联映射:某一主存块能映射到任一缓冲块,速度慢但cache利用率高。
  • 组相联:某一主存块能映射到某一缓存组中的任一缓冲块中。实际上如果每组都只有一块就等于是全相连,而如果只有一组那就是直接相联。优点就是兼顾速度与利用率。
  • 不同的相联模式在多层cache中分别使用:靠近CPU的cache要求高速度,可以用直接映射或者路数比较少的组相联映射;中间层次可以组相联(比如:两路组相联、四路组相联等);越远的可以用全相连,因为远的对速度要求低,对利用率要求高。

替换算法

  • 先进先出(FIFO)算法:不能很好的体现程序的局部访问原理
  • 近期最少使用(LRU)算法:希望替换cache块中CPU不再用,或者距离下次CPU读写这个块时间最长的块。

一点感悟:

  • 学到现在从组成原理的设计上学到一个很好的思路,在解决一个问题的时候如果有明显的两个极端的方案,那么可以在局部采用一个方案,而在整体采用另外一个方案。这个就能很好的融合两者的优点,而尽量较少弊端带来的影响。