计算机组成原理:CPU Cache(高速缓存)

为什么需要CPU Cache

在这里插入图片描述

为了弥补CPU和内存之间的性能差异,以便于能够真实变得把CPU的性能提升利用起来,而不是让它在那里空转,我们在现代CPU中引入了高速缓存。

从CPU Cache被加入到现有的CPU里开始,内存中的指令、数据,会被加载到L1-L3 Cache中,而不是直接从CPU访问内存中取拿。

CPU从内存读取数据到CPU Cache的过程中,是一小块一小块来读取数据的,而不是按照单个数组元素来读取数据的。这样一小块一小块的数据,在CPU Cache里面,叫做Cache Line(缓存块)

在我们日常使用的 Intel 服务器或者 PC 里,Cache Line 的大小通常是 64 字节。

知道了为什么需要CPU Cache,接下来,我们就来看一看,CPU究竟是如何访问CPU Cache的,以及CPU Cache是如何组织数据的

Cache的数据结构和读取过程是怎么样的

现代CPU进行数据读取的时候,无论数据是否已经存储在Cacha中,CPU时钟会首先访问Cache。只有当CPU在Cache中找不到数据的时候,才会去访问内容,并且将读取到的数据写入Cache中。当时间局部性原理其作用后,这个最近刚刚被访问的数据,会很快再次被访问。而Cache的访问速度远远快于内存,这样,CPU花在等待内存访问上的时间就大大变短了。
在这里插入图片描述
这样的访问机制,和我们自己在开发应用系统的时候,“使用内存作为硬盘的缓存”的逻辑是一样的。在各类基准测试(Benchmark)和实际应用场景中,CPU Cache 的命中率通常能达到 95% 以上。

问题来了,CPU如何知道要访问的内存数据,存储在Cache的哪个位置呢?接下来,从最基本的直接映射 Cache(Direct Mapped Cache)起,了解整个 Cache 的数据结构和访问逻辑:

  • CPU访问内存数据,是一小块一小块数据来读取的。对于读取内存中的数据,我们首先拿到的是数据所在内存块(block)的地址。而直接映射Cache采用的策略,就是确保任何一个内存块的地址,时钟映射到一个固定的CPU Cache地址(Cache Line)。而这个映射关系,通常用mod运算来实现。
  • 比如说,我们的主内存被分成0-31号这样的32个块。我们一共有8个缓存块。用户想要访问第21号内存块。如果21号内存块内容在缓存块中的话,它一定在5号缓存块(21 mode 8 = 5)中。
    在这里插入图片描述
  • 实际计算中,有一个小小的技巧,通常我们会把缓存块的数量设置成 2 的 N 次方。这样在计算取模的时候,可以直接取地址的低 N 位,也就是二进制里面的后几位。比如这里的 8个缓存块,就是 2 的 3 次方。那么,在对 21 取模的时候,可以对 21 的 2 进制表示10101 取地址的低三位,也就是 101,对应的 5,就是对应的缓存块地址

在这里插入图片描述

  • 取Block地址的低位,就能得到对应的Cache Line地址,除了21号内存块之外,13号、5号等很多内存块的数据,都对应着5号缓存块。既然如此,假如现在 CPU 想要读取 21号内存块,在读取到 5 号缓存块的时候,我们怎么知道里面的数据,究竟是不是 21 号对应的数据呢?
    • 这个时候,在对应的缓存块中,我们会存储一个组标记(Tag)。这个组标记会记录,当前缓存块内存储的数据对应的内存块,而缓存块本身的地址表示访问地址的低N位。就像上面的例子,21 的低 3 位 101,缓存块本身的地址已经涵盖了对应的信息、对应的组标记,我们只需要记录 21 剩余的高 2 位的信息,也就是 10 就可以了。
    • 除了组标记之外,缓存块中还有两个数据。一个自然是从主内存中加载来的实际存放的数据,另一个是有效位(valid bit)。啥是有效位呢?它其实就是用来标记,对应的缓存块中的数据是否有效的,确保不是机器刚刚启动的时候的空数据。如果有效位是0,无论其中的组标记和 Cache Line 里的数据内容是什么,CPU 都不会管这些数据,而要直接访问内存,重新加载数据
    • CPU在读取数据的时候,并不是读取一整个Block,而是读取一个它需要的整数。这样的数据,我们叫做CPU里的一个字(word)。具体是哪个字,就用这个子在整个Block里面的位置来决定。这个位置,我们叫作偏移量(Offset)。

总结一下,一个内存的访问地址,最终包括高位代表的组标记、低位代表的索引,以及在对应的 Data Block 中定位对应字的位置偏移量。
在这里插入图片描述

而内存地址对应到Cache里的数据结构,则多了一个有效位和对应的数据,由“索引+有效位+组标记+数据”组成。如果内存中的数据已经在CPU Cache里了,那一个内存地址的访问,就会经历下面4个步骤

  1. 根据内存地址的低位,计算在 Cache 中的索引;
  2. 判断有效位,确认 Cache 中的数据是有效的;
  3. 对比内存访问地址的高位,和 Cache 中的组标记,确认 Cache 中的数据就是我们要访
    问的内存数据,从 Cache Line 中读取到对应的数据块(Data Block);
  4. 根据内存地址的 Offset 位,从 Data Block 中,读取希望读取到的字。

如果在 2、3 这两个步骤中,CPU发现,Cache中的数据并不是要访问的内存地址的数据,那CPU就会访问内存,并把对应的Block Data更新到Cache Line中,同时更新对应的有效位和组标记的数据。

可以看到:通过巧妙的将内存地址,拆分成“索引+组标记+偏移量”的方式,使得我们可以将很大的内存地址,映射到很小的CPU Cache地址里。

另外:除了直接映射 Cache 之外,我们常见的缓存放置策略还有全相连 Cache(Fully Associative Cache)、组相连 Cache(Set AssociativeCache)。

你确定你的数据更新了吗?

CPU不仅要读数据,还需要写数据,问题是,CPU在写入数据的时候,怎么既不牺牲性能,又能保证数据的一致性呢?

我们现在用的 Intel CPU,通常都是多核的的。每一个 CPU 核里面,都有独立属于自己的L1、L2 的 Cache,然后再有多个 CPU 核共用的 L3 的 Cache、主内存。

在这里插入图片描述
第一个问题是:我们写入的数据,到达应该写到Cache里还是主内存呢?如果我们直接写入到主内存里,Cache里的数据是否会失效呢?为了解决这些问题,我们需要先了解两种写入策略

写直达(Write-Through)

最简单的一种写入策略,叫做写直到。在这个策略里,每一次数据都要写入到主内存里。

  • 写入前,我们会先去判断数据是否已经在Cache里面了
  • 如果数据已经在Cache里面,我们就先把数据写入更新到Cache里面,再写入到主内存中
  • 如果数据不在Cache里面,我们就只更新主内存

写直到这个策略很直观,但是问题也很明显,那就是这个策略很慢。无论数据是不是在Cache里面,我们都需要把数据写到主内存里面。

在这里插入图片描述

写回(Write-Back)

这个时候,我们就想了,既然我们去读数据也是默认从 Cache 里面加载,能否不用把所有的写入都同步到主内存里呢?只写入 CPU Cache 里面是不是可以?

当然是可以的。在 CPU Cache 的写入策略里,还有一种策略就叫作写回。这个策略里,我们不再是每次都把数据写入到主内存,而是只写到CPU Cache里。只有当CPU Cache里面的数据都要被“替换”的时候,我们才把数据写入到主内存里面去

写回策略的过程是这样的:

  • 如果发现我们要写入的数据,就在CPU Cache里面,那么我们就只是更新CPU Cache里面的数据
  • 同时,我们会标记CPU Cache里的这个Block是脏(Drity)的。所谓“脏”,就是指这个时候,我们的CPU Cache里面的这个Block的数据,和主内存是不一致的。
  • 如果我们发现,我们要写入的数据所对应的 Cache Block 里,放的是别的内存地址的数据,那么我们就要看一看,那个 Cache Block 里面的数据有没有被标记成脏的。
    • 如果是脏的话,我们要先把这个 Cache Block 里面的数据,写入到主内存里面。
    • 然后,再把当前要写入的数据,写入到 Cache 里,同时把 Cache Block 标记成脏的。
    • 如果 Block 里面的数据没有被标记成脏的,那么我们直接把数据写入到 Cache 里面,然后再把 Cache Block 标记成脏的就好了。
  • 在用了写回这个策略之后,我们在加载内存数据到 Cache 里面的时候,也要多出一步同步脏 Cache 的动作。如果加载内存里面的数据到 Cache 的时候,发现 Cache Block 里面有脏标记,我们也要先把 Cache Block 里的数据写回到主内存,才能加载数据覆盖掉Cache

在这里插入图片描述
可以看到,在写回这个策略里,如果我们大量的操作,都能够命中缓存。那么大部分时间里,我们都不需要读写主内存,自然性能会比写直达的效果好很多。

小结

  • 然而,无论是写回还是写直达,其实都还没有解决多个线程,或者是多个 CPU 核的缓存一致性的问题。这也就是我们在写入修改缓存后,需要解决的第二个问题。
  • 要解决这个问题,我们需要引入一个新的方法,叫作 MESI 协议。这是一个维护缓存一致性协议。这个协议不仅可以用于CPU Cache之间,也可以广泛用于各种需要使用缓存,同时缓存之间需要同步的场景下。
  • 7
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值