CPU缓存 - 维基百科
在处理器看来,缓存是一个透明部件。因此,程序员通常无法直接干预对缓存的操作。但是,确实可以根据缓存的特点对程序代码实施特定优化,从而更好地利用缓存。
双端口RAM和多模块存储器提高了存储器的工作速度,但是CPU速度太快了,所以存储器的速度还可以进一步优化,此时我们需要速度更快的存储单元,再结合程序的局部性原理,所以我们引入了"Cache-主存"机制,即引入Cache层。
上面我们提到了存取速度更快的存储单元,我们之前说的双端口RAM和多模块存储器指的是主存,属于DRAM,这里我们可以把DRAM换成SRAM,也就是Cache,速度更快,但是造价更高,当然Cache的容量也更小,一般就几十M,在windows的资源管理器里也可以看到自己电脑的Cache有多大。
Cache和主存的存取速度一般是数量级的差距。(Cache的速度可以比主存快上几十甚至上百倍)
windows下查看高速缓存存储器的容量。
程序的局部性分为空间局部性和时间局部性。简单来说就是CPU访问了一个地址A,CPU之后访问的地址大概率会在A的附近,这就是空间局部性。还有CPU最近未来要用到的信息多半就是现在正在使用的信息(比如循环大概率会重复访问一些存储单元),这就是时间局部性。
程序局部性+SRAM引出了Cache层。既然我现在使用的信息大概率在不久的将来会用到,那就可以把当前地址附近的数据都放入Cache中,使得CPU在同等时间内可以计算更多的数据(同等时间内从Cache中取得比内存更多的数据),也就使得计算机处理任务的速度更快了。
上面提到我们把内存里的部分数据放入Cache中使得CPU可以处理更多的数据,但是Cache的容量很小,不可能放下所有需要的数据,所以CPU要访问的数据可能没有放在Cache里面,而依然在主存里面。所以我们可以引出命中率, 命中率 H : C P U 要访问的信息已在 C a c h e 中的比例,缺失率 M = 1 − 命中率 H 命中率H:CPU要访问的信息已在Cache中的比例,缺失率M=1-命中率H 命中率H:CPU要访问的信息已在Cache中的比例,缺失率M=1−命中率H,从这里也可以看出如果我们提高缓存的命中率可以使得CPU更快的处理数据,提高系统性能。
Cache—主存 系统的平均访问时间t=Htc+(1-H)(tc+tm) ,先访问Cache,没命中再去访问主存
H是Cache的命中率
tc是CPU访问Cache的耗时
(1-H)是缓存的缺失率,tm是CPU访问主存的耗时,tc+tm的意思是访问Cache没找到要的数据,再去访问主存,所以Cache没命中访问主存花费的时间是(1-H)(tc+tm)
t=Htc+(1-H)tm,这种情况的t指的是Cache和主存同时访问,若Cache命中则立即停止访问主存,这种策略性能略高。
下面贴王道的课件
把目前这个主存地址周围的数据调入Cache中,怎么来定义这个周围?采用分块的方式,比如主存可以以1KB为一个块,然后以块为单位与Cache进行数据交换。比如图中就把主存地址分为块号和块内地址,主存地址存在很多种划分,这些都是逻辑上的,物理上由计算机硬件实现的。软件也可以实现地址划分,比如操作系统里的虚拟内存就是操作系统这个软件实现的。
解决Cache与主存数据块的对应关系。
王道的课件:
前面提到了主存以块为单位来和Cache进行数据交换
图中给了三种策略,分别是全相联映射,直接映射和组相联映射。
全相联映射、直接映射和组相联映射都需要标记位和有效位来区分Cache中放的是哪个主存块。有效位的出现是因为初始时标记位都为0,那标记位为0是表示没存数据还是主存块为0的数据?这就出现了歧义,所以引入了有效位。
主存与Cache的映射方式不同,标记位的位数可能也不同
特别注意主存地址的划分
将地址的前x位(表示块号)和Cache的所有块号进行比对,若标记位匹配且标记位为1则表示Cache命中,否则表示Cache不命中需要访问主存。其中匹配等功能由硬件电路实现,比如比较器。
从图中可知,Cache每行26B,即每个块大小为26B,主存大小256M=228B,所以主存块的编号是0 - 222-1,所以标记位需要22位,还有有效位1位。主存地址就可以被划分为前22位表示块号,后面6位表示块内地址。
访问一个主存地址时时把这个地址的前22位与Cache里的所有块的标记位进行比较,若匹配上了且标记位为1那就说明数据在Cache里,Cache命中,否则就得访问主存去拿到这个地址对应的数据。
注意地址的划分。
将地址的前x位和Cache的所有块号进行比对,若匹配且标记位为1则表示Cache命中,否则Cache不命中需要访问主存。
这个例子和全相联映射的例子差不多,不过相比于全相联映射需要的22位标记位,直接映射只需要19位。
因为采用取余的办法得到Cache块的位置,所以块地址的后三位就是取余后的结果,所以Cache块的编号天然地可以充当块地址的后三位。所以这里的标记只需19位。
匹配时根据地址块号的后三位确定Cache行,之后再将主存地址与Cache的前19位地址进行比对,若成功匹配且有效位为1则Cache命中。否则Cache未命中需要访问主存。
行号也叫块号、字块号等,标记位也称为Tag,还要注意块内地址是以字节编址的。
将地址的前x位和Cache的所有块号进行比对,若匹配且标记位为1则表示Cache命中,否则Cache不命中需要访问主存。
和直接映射类似。
匹配时根据地址块号的后两位确定所属的Cache组,之后再将主存地址与其所属分组内的所有Cache块的标记位地址进行比对,若成功匹配且有效位为1则Cache命中。否则Cache未命中需要正常访问主存。
优点 | 缺点 | |
---|---|---|
全相联映射 | 缓存空间利用率高,命中率高 | 比对标记最慢,可能比较所有标记 |
直接映射 | 查找标记的速度最快 | 缓存空间利用率低,命中率低 |
组相联映射 | 两种方式的折中,综合效果好一点 | 两种方式的折中 |
解决Cache很小,主存很大,Cache满了怎么办的问题。
采用替换的算法。
Cache替换算法则是指当Cache缓存已满时,如何确定哪些数据应该被替换出Cache,以为新的数据腾出空间。
替换时机 | |
---|---|
全相联映射 | Cache满了就选择替换某一块 |
直接映射 | 没有选择,直接替换地址对应的Cache块 |
组相联映射 | 组满了后再来一个块就需要替换。 |
抖动现象:刚刚调入Cache的块马上又被替换出去了,出现了一个块频繁地被换入换出这就是抖动。
Random
看这个例子就能懂了,有疑问的可以看王道的视频讲解。
随机找一个替换,实现简单,但是效果不稳定
First In First Out
最先进来的最先被替换,没考虑局部性原理,效果不怎么好。我们要做到最频繁访问的块尽可能的留在Cache里不被替换
Least Recently Used
采用计数器实现,计数器的位数记作n,2n就是Cache块的块数。每个块的计数都不同。
规则是:
让比这个计数的更小+1,可以理解为计数为0的位置归了命中行,原本比这个计数小的都要往上“挪”一个位置。
实际运行的效果优秀,也是最常使用的算法。淘汰最久没有使用的块是合理的。但是当频繁访问的块大于Cache块的数据很有可能发生抖动。(一般会采用双端链表来实现LRU算法,链表的长度固定,频繁访问的数量大于链表的长度会导致一些块频繁在链表里出现又消失,也就是抖动)。
与newbing的对话
Least Frequently Used.
也是采用计数器实现,块的计数可能相同。
命中一次对应的计数器就加一。淘汰时找命中次数最少的,若有很多个块的命中次数相等且都是最少,比如很多个命中次数为0的,此时可结合FIFO来进行淘汰。
运行效果不如LRU,因为曾经经常被用到的块不一定在未来被用到。但也不是所有场景都不如LRU,比如访问热点数据LFU的效果就比较好,所以具体选择哪种替换策略得根据场景的需求来。
算法 | 优点 | 缺点 |
---|---|---|
RAND | 实现简单 | 效果不稳定,通常不如LRU |
FIFO | 实现简单 | 不考虑局部性,可能导致Cache命中率降低 |
LRU | 考虑了局部性,可以提高系统性能,效果优秀 | 实现复杂,Cache较小时可能会发生抖动 |
LFU | 某些场景下效果优秀 | 实现复杂,Cache较小时可能会发生抖动 |
CPU修改了Cache的数据副本,如何确保主存中数据母本的数据一致性?Cache的写策略
Cache的写策略只用考虑写命中和写不命中的情况。读命中和读不命中都不会更改数据,所以无需考虑。
非写分配法:写不命中时,CPU只将数据写入主存,不调入Cache。
这两种策略通常在一起使用。
写分配法:写不命中时将主存里的块先调入Cache,在Cache中修改,一般搭配写回法。
与操作系统内容有关(课件我也没找到,所以等到操作系统再深究,现在先简单了解)
与操作系统内容有关(课件我也没找到,所以等到操作系统再深究,现在先简单了解)
注意Cache和主存的映射方式中主存地址的划分。
1KB=210B=213b,块内地址的位数是10,而不是13,因为一般以字节编址。
采用直接映射时,Cache的地址就等于块号+块内地址。
写选择计算题时可以先把cache和主存的容量写出来,然后把它们的标记位,块号或者组号这些划分出来。
cache行里有主存数据、标记位、脏位、替换控制位等。替换控制位常被用于LRU、LFU算法里面,脏位是回写法的。
给一个主存地址问数据对应cache的哪个块其实就是问地址的划分。
有虚拟存储器才会有虚拟地址,CPU在高速缓存、主存、硬盘构成的体系中访问存储系统发送的是主存物理地址。
LRU可以和组相联等映射联合起来出题,如果是组相联需要先分组再进行LRU的模拟。
指令Cache和数据Cache的分离的主要目的是减少指令流水线资源冲突
8路组相联说明一组里面有8个cache行,那比较一个组就需要并行的使用8个比较器,比较器的位数就是标记位(Tag)的位数。
包含循环的程序计算Cache命中率不能算容量,得算访问Cache的次数以及命中多少次,而不是算访问的主存容量与Cache容量的比值。