> 文章列表 > Cache;高速缓冲存储器

Cache;高速缓冲存储器

Cache;高速缓冲存储器

高速缓冲存储器

概述

​ 在多体并行存储系统中,由于IO设备向主存请求的级别高于CPU访存,这就出现了CPU等待IO设备访存的现象,导致CPU空等一段时间,甚至等待几个周期,从而降低了CPU的工作效率,为了避免CPU和IO设备争抢访存,可在CPU与主存之间加一级缓存,这样主存可将CPU要取的数据提前送到缓存,一旦主存在与IO设备交换时,CPU可直接从缓存中读取所需的数据,不必空等而影响效率。另一方面Cache 也可以来解决主存与CPU速度的不匹配问题

根据程序运行的局部性原理,虽然Cache的容量远小于主存。但只要把近期要用的数据提前从主存送到Cache中,那么就可以做到CPU在一定的时间内只访问Cache,从而节省了时间。

实际系统中的Cache

image-20230417135250626

上图展示了系统存储器的架构,包括了CPU的寄存器,L1/L2/L3/L4/ Cache,DRAM和硬盘。数据访问时要是没有找到数据则从左向右找,最后找到硬盘中。

img

其中要注意的是CPU和Cache是以字为单位传输的,而Cache到主存是以块传输的。

现有的Cache的一般组成如下

image-20230417134337290

​ 将Cache按数据类型来分可分为:I-CacheD-Cache。其中I-Cache负责放置指令,D-Cache负责存放数据。两者最大的不同是D-Cache里面的数据可以写回,I-Cache是只读的(因为程序运行时,其运行的指令不会被修改)。当然还有将Cache按照其他标准的分类的,例如大小、位置、数据关系……

下图为Cache的基本结构原理框图。Cache主要由Cache存储体、地址映射变换机构、Cache替换机构几大模块组成。

IMG_20230417_140007

Cache存储体

​ Cache存储体以为单位与主存交换信息,Cache与CPU之间以为单位进行数据交换,且Cache 的访存优先级最高。

地址映射变换机构

​ 地址映射变换机构是将CPU松籁的主存地址转换为Cache地址。由于主存和Cache的块号大小相同,块内地都是相对块的其实地址的偏移量(即低位地址相同)。因此地址变换主要是主存的块号(高位地址)与Cache块号之间的转换。而具体如何进行转换就是Cache——主存地址映射

替换机构

​ 当Cache内容已满,无法接受来自主存块的信息时,就由Cache内的替换结构按一定的替换算法来确定将Cache中的那个块移出去主存,而把新的主存块调入Cache。需要注意的是,Cache对用户是透明的,即编程时用到的地址是主存地址,用户不知道这些主存块是否已经调入Cache内。

Cache的读写操作

​ Cache读操作的过程可由下流程图来表述。当CPU发出主存地址后,首先判断该存储字是否在Cache中。若命中,直接访问Cache,将该字送至CPU;若未命中,一方面要访问主存,将该字传给CPU,与此同时要将该字所在的主存块送至Cache中,若果此时,Cache已经装满,就需要执行替换算法。

IMG_20230417_141507

Cache 的工作原理

要了解Cache的工作原理,我们需要回答4个问题:

  • 地址映射。如何区分Cache与主存的数据块对应关系?
  • 数据如何查询
  • 替换算法。Cache比主存小,Cache满了怎么办?
  • 写策略,CPU修改了Cache中的数据副本,怎么保证如主存中的数据木本保持一致?

Cache——主存的地址映射

​ 由于Cahce的行数远小于内存块数,因此主存中只有一部分块的信息可放在Cache中,因此在Cache中需要为行添加一个标记,指明它是主存中的哪一块的副本。该标记的内容相当于主存中块的编号。同时为说明Cache行中的信息是否有效,每个Cache行需要一个有效位。

直接映射法

映射的关系为:
i=jmodC或  i=jmod2ci = j \\,\\, mod \\, \\, C \\ \\\\ 或 \\ \\ i = j \\ mod \\ 2^c i=jmodC   i=j mod 2c
其中,i为缓存块号,j为主存块号,C为缓存块数。

image-20230417143624934

全相联映射

允许主存中的每一字块映射到Cache中的任何一块位置上。使用这种方法时命中率更高,但是要找出对应块是否在Cache中则需要检查所有的Cache块。这种比较通常采用按内容寻址的存储器。

Cache;高速缓冲存储器

组相联映射

​ 组相联映射是相对直接映射和全相联映射的一种折中。它把Cache分为Q组,每组有R块,并有以下关系:
i=jmodQi \\ = \\ j \\ mod \\ Q i = j mod Q
其中,i为缓存组号,j为主存块号。用这个公式可能还不是很好理解,我们可以看下图:

Cache;高速缓冲存储器

根据这个图,我们可以来理解组相联和直接相联的区别。对于组相联映射,与直接相联的最大区别就是每个分组中可以方多个块,也就是说主存映射到Cache时,每个主存块有唯一对应的Cache组(体现了组相联映射),同时这个主存块可以对应这个Cache组中的任意一块(体现了全相联映射)。

Cache;高速缓冲存储器

考试要点小结

主存地址与Cache工作时各字段的含义

首先是主存地址在与Cache对应时的字段含义:

三种映射方式都有的块内地址:
块内地址数=log2块的存储容量编址单位容量块内地址数 = log_2 \\frac{块的存储容量}{编址单位容量} 块内地址数=log2编址单位容量块的存储容量
全相联映射

image-20230417204532555

其中的主存块号就是地址位数 - 块内地址。

直接映射

image-20230417204704257

其中行号对应的位数:
行号对应的位数=log2行数行号对应的位数 = log_2 行数 行号对应的位数=log2行数
而其中的行数:
行数=Cache容量块大小行数 = \\frac{Cache容量}{块大小} 行数=块大小Cache容量
最后的标记位数即为地址位数减去主存块号减去块内地址
标记位数上=地址位数−行号位数−块内地址位数标记位数上 = 地址位数 - 行号位数 - 块内地址位数 标记位数上=地址位数行号位数块内地址位数
组相联映射

image-20230417205221478

这个其实和直接映射计算类似,不同的就是组号的位数,例如对n路组相联:
组号位数=log2Cache容量n×块大小或组号位数=log2组数组号位数 = log_2 \\frac{Cache容量}{ n \\times 块大小 } \\\\ 或 \\\\ 组号位数 = log_2 组数 组号位数=log2n×块大小Cache容量组号位数=log2组数

在Cache实际进行存储时,每个Cache行存储的格式为标记Tag+字块数据,这里的标记包括主存地址中的标记Tag,还可能包含的是有效位、一致性维护位、替换算法控制位,这几种需要根据题目的条件,但是地址中的Tag和有效位一般是必定存在的。

Cache;高速缓冲存储器

对应这个Cache行的结构也可以知道,在Cache进行存储时,✨✨✨实际上并不会存储行号、组号、块内地址号,而只会在Cache行中存储Tag+其它标志位。这是为什么呢?以下是个人的一些理解:

为什么不存行号、组号呢?

因为给你一个主存地址要识别属于哪一组或者哪一行,这个信息已经包含在这个主存地址内了,Cache和主存的架构说逻辑电路已经可以自动找到,这个内存地址对应的到底是哪一行或者哪一组。所以不需要Cache存储行号或组号,这样也可以增加Cache的利用率。

为什么不存块内地址号?

Cache中的一个块中存储多个存储字长。块为Cache的存储单元。而内存地址号则只是用于在访存时,可以在块中找到对应的存储字。所以Cache是不可能存储访问块号的。

为什么需要存储标记Tag呢?

Tag是内存地址在采用不同映射算法后,地址最短的唯一标志。例如采用直接映射后,能在同一行中存储的所有内存地址的Tag位不会重复,组相联映射也是如此。而只有Cache行中存储了对应数据的Tag,才可以在用一个地址去访问的时候确定是否hit。总的来说Tag用于确定对应内不差那地址的数据是否在Cache中。

比较器数量
映射方法 比较器数
直接相联 1
全相联 Cache行数
组相联 组内块数

直接相联映射时,需要的比较器数为1个,示意图如下:

Cache;高速缓冲存储器

全相联中比较器个数为行数。如下示意图

Cache;高速缓冲存储器

二路组的结构如下图,其中比较器标记为=?,所以组相联的比较器数为组内块数

Cache;高速缓冲存储器

其它

Cache;高速缓冲存储器

替换策略

​ 理想的替换算法是把未来很少用到的或者很久才会用到的数据块替换出来,但实际上很难做到。常使用的替换算法有先进先出算法、近期最少使用算法和随机算法。

​ 不过当Cache采用不同的映射方法时也决定了替换的算法:

  • 直接映射。如果对应行非空,则直接替换,无需替换算法
  • 全相联映射。在全局中选择替换哪一块
  • 组相联映射。分组满了才需要替换需要在分组内选择替换哪一块。(从这个角度也可以看出组相联映射的优点)

Cache;高速缓冲存储器

随机替换算法(RAND)

随机替换算法(RAND)。若Cache已满,则随机选择一块替换。

先进先出(FIFO)

FIFO算法选择最早调入Cache 的字块进行替换。

Cache;高速缓冲存储器

✨✨✨近期最少使用(LRU)

LRU算法——为每个Cache块设置一个计数器,用于记录每个Cache块已经有多久没被访问了。当Cache满后替换计数器最大的。当频繁访问的主存块数大于Cache行的数量时,可能会发生抖动。

计数器数值改变的三种情况:

  1. 命中时,所命中行的计数器清零,比其低的计数器加1,其余不变
  2. 未命中且还有空闲时,新装入的行的计数器置0,其余非空闲行全加1;
  3. 未命中且无空闲行时,计数器最大的行的信息块被淘汰,新装行的计数器置0,其余全加一。

✨✨✨Cache块的总数2n2^n2n时,则计数器只需n位。这是上述计数器数值改变中情况一的结果,只当命中时,只增加计数器比其低的,其余不发生变化,让Cache重复被命中时,其余行对应的计数器数值增长的不会过大

Cache;高速缓冲存储器

最不经常使用算法(LFU)

最不经常使用算法(LFU)——位每个Cache块设置一个计数器,用于记录每个Cache块被访问过过几次。当Cache满后替换计数器最小的

计数器数值改变的情况:

新调入的块的计数器=0,之后每次被访问计数器+1,。需要替换时,选择计数器最小的一行。

LFU算法并没有很好的遵循局部性原理,因此实际运行效果不如LRU。

Cache;高速缓冲存储器

Cache 写策略

因为Cache中的内容是主存块的副本,当对Cache中的内容进行更新时,就需要选用写策略使Cache内容和主存内容保持一致。

对于Cache写命中,有两种处理方法:

  1. 全写法(写直通法)。当CPU对Cache写命中时是,必须把数据同时写入Cache和主存。当某一块需要替换时,不必将这块写回主存,用新调入的块直接覆盖
  2. 回写法。当CPU对Cache写命中时,只把数据写入Cache,而不直接写入内存,只有当此块被换出时才写回主存。这种方法减少了访存的次数。为了减少写回主存的开销,每个Cache行设置一个修改位(脏位),来标志该块是否被修改。

对于Cache写不命中:

  1. 写分配法(write-allocate)。将对应的内存块调入Cache,然后跟新这个Cache块的内容
  2. 非写分配法(not-write-allocate)。当CPU对Cache写不命中时只写入内存,不调入Cache。搭配全写法使用。

非写分配法通常和全写法合用,写分配法通常和写回法合用。

现代计算机的Cache通常设立多级Cache,假定3级Cache,按离CPU远近可命名位L1,L2,L3,离CPU越远,访问速度月满,容量越大。指令Cache和数据Cache分离一般在L1,,通常对L1对L2Cache采用全写法,L2对主存使用回写法。不过其实实际现实应用时还需考虑多Cache数据一致的问题,如下图多核CPU时,如何保证CPU_A和CPU_C对应的指令数据Cache一致也是个问题,想要了解可阅读 深入理解CPU cache:组织、一致性(同步)、编程

Cache;高速缓冲存储器

全写法的Write Buffer

全写法是当命中Cache时,同时修改Cache和内存中的数据。为了达到同时修改,需要在Cache和Memory之间增加一个Write Buffer

  • CPU:同时写数据到Cache和Write Buffer
  • Memory controller(存控):将缓冲内容写入主存

Cache;高速缓冲存储器

Cache;高速缓冲存储器

​ 上面两张图片讲的就是写缓冲饱和溢出的问题。要理解写缓冲饱和可以将Write Buffer看作是一个队列,这个队列的长度有限。单层Cache时出队的速度是DRAM写周期,而入队的速度为CPU时钟周期。而CPU时钟周期<DRAM写周期,即入队速度大于出队速度。当短时间内大量入队请求时(即大量写指令),就会导致队列的长度增长且超过限制,即写缓冲饱和。

​ 而其中加一个二级Cache的解决策略,其实就是让出队的速度增快,因为此时全写法是将数据写入到L2 Cache中,从而缓解了入队速率和出队速率不平衡的问题。当然还有一个解决方案就是直接采用写回法。

参考

  • 深入理解Cache
  • 《王道计算机组成原理考研复习指导》
  • 《计算机组成原理 第3版》
  • http://staff.ustc.edu.cn/~llxx/cod/courseware/2022ppt-9.pdf
  • https://cs.nju.edu.cn/swang/CompArchOrg_13F/slides/lecture10.pdf