理解计算机Cache

理解计算机Cache

如果我们仔细的查看芯片架构,就会发现里面都有缓存( MCU 可能没有缓存),可见缓存的重要性。本文将详细的介绍缓存的基础知识。

1. 什么是缓存

首先考虑一种标量访问的情况。就像下面这段代码,它重复地访问同一个标量。在这种情况下,存储访问会出现需要访问的地址不随时间变化的特点。

a = 10
for i in range(5):
    print(a)

如果我们考虑数组访问,比如顺序访问数组中的每个元素。那么存储访问会出现需要访问的地址随时间线性变化的特点。

观察这两种不同的访问情况,我们可以发现,内存访问并非是完全随机的,这背后体现的是局部性原理。局部性原理可以分为两类。一个是时间局部性,一个元素一旦被访问到,很可能在短时间内再次被访问到。另一个是空间局部性,一个元素周围的元素很有可能会接下来被访问到。局部性原理至关重要,确保了缓存能够有效运作。

接下来我们关注一个最基础的处理器内存模型。当处理器需要读取数据时,它通过一个特定的地址访问内存,内存响应这一请求并将数据返回。然而,存储器的一个有趣特性是它离处理器越远,容量通常越大,但速度却越慢。这种速度的差异在处理器和内存之间依然很明显。

为了解决速度差异带来的挑战,我们在处理器和内存之间引入了一个强大的中间者——缓存。当处理器寻找的数据恰好在缓存中时,就称为缓存命中。这时处理器可以迅速通过地址访问缓存中的数据。而如果所需数据不在缓存中,此时,缓存必须先从内存中获取相应的数据,然后再将它返回。我们称这种情况为缓存丢失。

2. 块

00123
14567
2891011
312131415
416171819
520212223
624252627
728293031
832333435
936373839
1040414243
1144454647
1248495051
1352535455
1456575859
1560616263

首先,我们来观察这个64字节的内存模型。在按字节编址的情况下,每个小格子代表一个字节,因此我们需要6个位来访问这64个格子。如果我们按字来编址,假设一个字等同于四个字节,那么每行就代表一个字,此时只需四位便可访问这16行。实际上块的概念与字的概念在本质上是相似的,比如我们可以设定一个块等同于1个字。这样内存的一行就相当于一个块。

0
1
2
3

让我们再来看一个四行的缓存模型。在这个模型中,缓存的每一行都对应一个块,因此一个内存块就可以直接放入缓存的任意一行。

内存
字00 块01 块02 块03 块0
字14 块05 块06 块07 块0
字28 块09 块010 块011 块0
字312 块013 块014 块015 块0
字416 块117 块118 块119 块1
字520 块121 块122 块123 块1
字624 块125 块126 块127 块1
字728 块129 块130 块131 块1
字832 块233 块234 块235 块2
字936 块237 块238 块239 块2
字1040 块241 块242 块243 块2
字1144 块245 块246 块247 块2
字1248 块349 块350 块351 块3
字1352 块353 块354 块355 块3
字1456 块357 块358 块359 块3
字1560 块361 块362 块363 块3
缓存
0
1

那么如果我们让一个块等于四个字呢?在这种情况下,四个内存行才能组成一个完整的块。我们给它们标上序号方便查看。此时展示一个两行的缓存模型,缓存行始终等于一个块的大小。在这种情况下,由四行内存组成的块能够被整体放进缓存的一行中。此时我们对内存地址可以进行重新的理解。目前有四个快,可以使用两位来进行索引。对于按字编址,一个块内有四个字,那么就是用两位作为偏移。对于按字节编址,对块的索引是一致的。而一个块内有16个字节,那么就使用四位作为偏移。

3. 缓存映射

内存
字00 块01 块02 块03 块0
字14 块05 块06 块07 块0
字28 块19 块110 块111 块1
字312 块113 块114 块115 块1
字416 块217 块218 块219 块2
字520 块221 块222 块223 块2
字624 块325 块326 块327 块3
字728 块329 块330 块331 块3
字832 块433 块434 块435 块4
字936 块437 块438 块439 块4
字1040 块541 块542 块543 块5
字1144 块545 块546 块547 块5
字1248 块649 块650 块651 块6
字1352 块653 块654 块655 块6
字1456 块757 块758 块759 块7
字1560 块761 块762 块763 块7

接着,就让我们尝试自己设计一个缓存。我们令一个块等于两个字,因此两个内存行就是一个块。后面出现的内存模型将会以块为基础单位。

缓存
0
1
2
3

让我们使用一个四行的缓存模型,我们都知道缓存行等于一个块,那么其中就包括了两个字,也就是八个字节。缓存的目的是通过内存地址返回给处理器数据,而处理器是不知道块的存在的。因此我们需要从块中找到对应的数据。如果此时是按字节编址,那么访问一个块中的八个字节就需要三个位,我们称之为偏移量。而如果是按字编址,那么访问一个块中的两个字就只需要一个位。

按字编址4位<=>16个字
标记Tag索引Index索引Index偏移量Offset

接下来观察这个四行的缓存。为了定位到其中一行的数据,我们需要通过两位来访问这四行,可以将其称之为索引。

内存
块0
块1
块2
块3
块4
块5
块6
块7

再拿出以块为一行的内存模型。当八个内存块对应到四个缓存行时,必然会有两个内存块映射到同一缓存行。为了解决这个问题,我们在编址时加入了一个标记位。但如果又有新的内存块要映射到这个已满的缓存行呢?单靠一位无法区分三个不同的内存块。为了解决这一问题,我们需要引入不同的映射策略。既然一个标记为只能区分两个内存块,那么我们就指定内存块存储的位置,直接映射(Direct-mapped)的思想就是这样。每两个内存块指向同一个缓存行,比如内存块0和内存块4指向缓存行0、内存块1和内存块5指向缓存行1,以此类推。这样一来,一个缓存行就只会有两个内存块存入,符合我们之前的设计。通过索引就能找到对应的缓存行,再根据标记就能找到对应的内存块。直接映射的优点是硬件设计简单,索引的存在使得查询速度快。缺点是每个缓存行的冲突概率较高,有很大可能会影响效率。

按字编址4位<=>16个字
标记Tag标记Tag标记Tag偏移量Offset

那我们就让每个缓存行区分更多的内存块,从而减少冲突的发生,直接区分全部内存块会怎么样?这正是全相联映射(Fully associative)的思想。一个内存块可以放入任意的缓存行,每个内存块都是如此。此时我们需要重新理解内存地址,只需要通过标记就能访问到对应的缓存行。因此不再需要索引,将索引位都改为标记,而三位的标记也对应着一个缓存行能够区分八个内存块。其优点很明显,冲突会特别少。但由于没有了索引,每次访问都需要和所有的标记进行比较,查询速度就会特别慢并且硬件设计也会很复杂。

二路组相联缓存
0组0
1组0
2组1
3组1
按字编址4位<=>16个字
标记Tag标记Tag索引Index偏移量Offset

因此,我们需要一个折中的策略,这就是组相联映射(Set-associative)。组的概念本质上与字相似。如果一个组中有一个块,即一个缓存行,我们就称之为一路组相联,就是现在没有区分的状态。而一个组里面有两个块,即两个缓存行,就称为二路组相联。这样我们可以通过一位的组索引来访问这两个组,此时的标记是两位,那么能区分四个内存块。一个内存块可以放到固定的一个组中,而在一个组中可以放置在任意一行。在组中就与全相联一样,因此我们只需要通过标记就可以在组里面访问到对应的内存块。而每个内存块都指定放入对应的组里面,这是直接相联的思路。组相联映射巧妙的平衡了冲突频率、查询速度和硬件设计的复杂度,是一种高效的映射策略。

4. 缓存结构

接下来让我们看一下内存与缓存在结构上的差异。

首先,内存的结构相对比较直观,它包含存储空间及其对应的地址,通过这些地址我们可以直接访问存储空间中的特定数据。而缓存同样具有存储空间,但与内存不同,缓存还需要控制信息,如有效位用于标示该行数据是否有效。此外还可以有其他的标志位,比如一个标示脏数据的位。一行的控制信息与存储数据一起被称为缓存槽。

我们来看一下内存的地址结构。首先能够以块为单位理解地址,分为块索引和块内偏移。而缓存能够对内存地址重新进行理解。以组相联映射为例,分为标记、组索引和偏移。其中的标记正是缓存中的控制信息,此时我们能够计算出缓存的总容量,就是缓存槽的大小乘以缓存总行数。一个槽中包含了有效位、标志位、标记和块容量。缓存的存储容量是另一个关键的概念,它指的是单个缓存槽的块容量与缓存总行数的乘积。在这个情况下,引入缓存地址的概念,由行索引和行内偏移组成。其本质和以块为单位对内存地址的理解是一致的。因此我们可以通过存储容量计算出缓存的地址位数。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值