计组学习笔记(四):高速缓冲存储器Cache

一、为什么要有Cache

  我们知道存储器的层次结构为,CPU——>寄存器——Cache——主存——磁盘——磁带、光盘,假设没有Cache这一层,那就相当于我CPU要想找东西直接去主存找,CPU速度很快,主存速度不行跟不上,我要一个东西它半天才给我,这显然是不效率的。另外,程序的转移概率不会很低,数据分布的离散性比较大,只依靠并行主存系统提高主存系统的带宽是有限的。为了解决这些问题,就有了Cache。站在CPU的角度来看,Cache的速度快,价格接近于主存,可以很好地解决这些问题。

二、Cache的中心思想

  Cache很好地解决了速度不匹配的问题,只要CPU想要得到的内容在Cache中,那么CPU就不必去主存中寻找。Cache的空间不大,为什么这么小点的容量就能实现提速呢?原因就在于程序访问的局部性原理。程序访问的局部性原理包括时间局部性和空间局部性,时间局部性指的是最近正在访问的信息可能在不久的未来还可能会被访问,因为程序存在循环嘛,一个循环里面的语句总是过一会就会执行一次;空间局部性指的是不久的未来可能需要的信息与现在正在访问的信息在存储空间上是邻近的,因为指令一般都是顺序存放顺序执行的,数据也一般都以数组等方式集中地存放。

  Cache就是利用的程序访问的局部性原理,我们把程序正在使用的部分存放在其中,因为局部性原理,CPU大部分的访存操作都是针对Cache进行,命中率比较大,使得程序的执行速度大大提高。(Cache中的内容来自于主存,只是主存中的内容的副本)

三、Cache的基本结构

  Cache由Cache存储体、地址映像变换机构、Cache替换机构几大模块组成。

  (1)Cache存储体:Cache存储体主要以块(Cache块又称Cache行)为单位与主存交换信息,每块由若干字节组成,块的长度称为块长(行长)。由于Cache的容量远小于主存,所以块数也是远小于主存的块数。主存则一般采用多体结构,加快Cache与主存之间的调动。

  (2)地址映像变换机构:它的作用是将CPU送来的地址转换为Cache地址,因为主存的块大小和Cache块大小是相同的,也就是说快内地址都是相对于块起始地址的偏移量,即低位地址是相同的。所以地址变换主要是内存地址的高位与Cache块号之间的转换,根据地址映像方式的不同,转换后的结果也不同。如果转换后的Cache块和主存建立的对应关系,则命中,说明CPU欲访问的内容存在于Cache中,可以直接访问Cache。反之如果没有与主存建立对应关系,则不命中,这时候CPU要去主存找信息,在取出这条信息后还要把这条信息所在的块调入Cache中,以便之后CPU的使用,当然这是Cache未满的状态下,如果Cache是满的状态则要选择合适的替换策略换出一块并将这块调入。

  (3)Cache替换机构:刚才提到,如果要将一块调入Cache时,Cache是满的状态,这时就要用替换机构按照一定的替换策略选中Cache中的一块调出,再把新块调入Cache。(Cache对用户是透明的,将主存块调入Cache的任务是由机器硬件自动完成)

四、Cache和主存的映射方式

  在Cache中,地址映射是指把主存地址空间映射到Cache地址空间,由于Cache块数远小于主存块数,这样主存中只有一部分块的内容可以放在Cache中,因此在Cache中要为每一块加一个标记,指明它是哪一块的副本。该标记的内容相当于主存中块的编号。此外每个标记还要设置一个有效位,有效位为1时表示映射的主存数据块有效,反正无效。

  主要的映射方式有一下三种:

直接映射

  主存数据块只能装入Cache中的唯一位置。若这个位置已有内容,则发生冲突,原来的块无条件被替换出去,不含替换算法。直接映射比较简单,但是不够灵活,使Cache中的许多其他块空着没用,空间利用率高,还易发生冲突。

  直接映射的关系可以定义为:j=i mod 2^c

  其中j是Cache块号(行号),i是主存的块号;2^c是Cache中的总块数。在这种映射方式中,主存的第0、2^c、2^(c+1)......块,只能映射到Cache的第0行,主存的第1、1+2^c、1+2^(c+1)......块,只能映射到Cache的第1行依次类推。

  直接地址映射的地址结构为:

  举个例子,如果Cache内有32个块,每个块长为64B,主存空间有4GB,按照字节编址,现在给出一个地址12345678H,我们来看看这个地址的结构,首先主存空间有4GB,即2^32B,按照字节编址就是32位地址,Cache内每个块有2^6B,那么每块就有6位块内地址,Cache有2^5个块,说明有5位Cache字块地址,剩下32-6-5=21位就用作主存字块标记,这样12345678H换为二进制地址为0001 0010 0011 0100 0101 0110 0111 1000,其中第0~5位为字块内地址111000,第6~10位为Cache字块地址110 01(第25块,从第0块开始数)剩下21位为主存字块标记0001 0010 0011 0100 0101 0。

  用图来表示直接映射:


全相联映射

  与直接映射是完全相反的,直接映射中,内存中每一块只能映射到Cache中固定的位置,而全相联映射是主存块可以装入Cache中任意的位置。所以,全相联映射比较灵活,空间利用率很大,也不容易发生冲突。当然它也有缺点,因为可装入任意块,所以它要采用昂贵的按内容寻址的相联寄存器来进行地址映射,地址变换速度慢。

  全相联映射的地址结构为:


  对比直接映射来看缺少了Cache字块地址这一部分,毕竟这里不需要块号。

  用图来表示全相联映射:


组相联映射:

  其实仔细一猜也会猜到组相联是怎么回事了——前两者的结合。组相联映射中,将Cache分成若干个组,每一组有若干个块,映射的时候,对于组来说,采取的是直接映射,主存中的每一块只能装入对应的一个组,组内采用全相联映射,内存中的块可以装入该组中的任意一块中。对于两个极端,如果组数等于Cache块数,就变成了组相联映射,如果组数等于2,就变成了直接映射。

  组相联映射的关系可以定义为:j=i mod Q

  其中,j为Cache的组号,i为主存块号,Q为Cache的组数。

  组相联的地址结构为:


  与直接映射同样举一个例子,如果Cache内有32个块,每个块长为64B,主存空间有4GB,按照字节编址,现在给出一个地址12345678H,如果采用四路组相联映射(四路就是四个块分一组),我们来看一看这个地址结构是怎么样的,首先我们看它分了几组,32块,4块1组,所以分了8组,这样就有2^3组,组地址就占了3位,字块内地址还是2^6B,占6位,剩余的就是主存字块标记,12345678H转化为二进制0001 0010 0011 0100 0101 0110 0111 1000,字块内地址为0~5位为111000,组地址为6~8位001,主存字块标记为9~31位0001 0010 0011 0100 0101 011。

  用图来表示组相联:


段相联映射

  段相联映射是直接映射与全相联映射的另一种组合方法,它是将内存和Cache都分成段,每段内的块数相等,段之间采用全相联映射,段内块采用直接映射。

五、Cache中主存的块的替换算法

  在我们采用直接映射方法进行地址映射的时候,是不需要替换算法的,如果有冲突也是直接无条件换出换入。但是采用全相联和组相联的时候,在Cache空间占满的情况下就要采用替换算法来将合适的块换出。替换算法主要有以下几种:

  (1)随机算法:随机确定替换的Cache块。实现简单,但是并没有根据程序访问的局部性原理,故命中率会比较低。

  (2)先进先出算法(FIFO):最早调入的块进行替换。它比较容易实现,但是也没根据程序访问的局部性原理,可能吧最早进入的循环程序给换出去。

  (3)近期最少使用算法(LRU):依据程序访问的局部性原理,选择一个近期长久没有访问的块,将其换出,命中率会比较高。

  (4)最不经常使用算法:将一段时间内使用次数最少的块替换,命中率也比较高。

  这些替换算法与操作系统中的页面置换算法等有些相似度,主要采用的思想大致相同。

六、Cache写策略

  对于读操作来说我们都已经知道CPU读操作的流程,但是对于写操作来说,由于Cache中的内容只是主存内容的副本,当对Cache中内容更新的时候,主存并没有更新,为了保持主存与Cache内容的一直,我们就有了对于写操作的策略:

  (1)全写法(写直通法、write-through)

  这种方法是,当对Cache写命中时,必须把数据同时写入Cache和主存中,这种方法实现简单,能随时保持主存数据的正确性。但是大大增加了访存次数,降低了Cache的效率。

  同时为了减少全写法直接写入主存的时间损耗,可在Cache和主存之间加一个写缓冲区,CPU同时对Cache和缓冲区写入数据,写缓冲区再控制将内容写入主存。当然如果写动作太频繁可能导致写缓冲区溢出。

  (2)写回法(write-back)

  这种方法与全写法相反,当对Cache写命中时,只更新Cache中的内容,待得这一块被替换出去的时候在写回主存,这种方法不需要大量大访存操作。但是存在不一致的隐患,如果一直不被替换出去,那它的内容与主存块中的内容就不一致。而且这种方法还需要给每一块配置一位“脏位”,如果脏位为1则表明发生过写操作,那么替换出去就要更新主存块。


  这两种方法都是对于单处理器的系统来保持Cache与主存内容一致的,对于多处理器的系统这两种方法可行不通。多处理器下我还不知道有什么策略。

  另外现在还普遍采用多个Cache,一是增加级数,二是将统一的Cache分成多个,比如说数据Cache和指令Cache。


  如有错误,还望指正~


发布了32 篇原创文章 · 获赞 53 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_39378221/article/details/80832796