后端缓存系统怎么设计(系统设计基础知识)
缓存是为了弥补高速设备和低速设备之间的差距而引入的中间层,以提高应用程序的处理速度。存储先前获取的数据或计算结果的副本可提高处理速度。
为什么计算机需要缓存?这是因为:
- 计算机系统中各种存储设备的容量、访问速度、成本差异很大
- 对缓存的深入理解有助于我们编写性能更好的代码。缓存是一个硬件概念,位于处理器与内存之间,主要用于解决处理器与内存速度不匹配的问题。因此,编写的程序必须具有良好的局部性,尤其是时间局部性和空间局部性。
让我们采用自下而上的方法来了解缓存的各个层。
我们都知道现在的CPU多核技术会有几级缓存,而现在的CPU会有3级内存(L1、L2、L3)。
1 级缓存( L1缓存)
它负责缓存指令和缓存数据。一级缓存的容量和结构对CPU的性能影响很大,但由于其结构复杂。它大约为 256 KB。
2 级缓存( L2缓存)
L2缓存的容量也会影响CPU的性能。更大容量的二级缓存可以为 CPU 提供更好的系统性能。比如Intel的第八代i7-8700处理器有6个核心,每个核心都有256 KB的二级缓存,每个核心都独享,这样二级缓存的总数就达到了1.5MB。
3 级缓存( L3缓存)
它负责进一步降低内存的延迟,提高海量数据计算的性能。三级缓存由核心共享,可以做大容量。
笔记:
- L1缓存分为2种类型,即指令缓存和数据缓存。L2 缓存和 L3 缓存不区分指令和数据。
- L1 和 L2 缓存在每个 CPU 内核中,L3 由所有 CPU 内核共享。
- L1、L2、L3离CPU越近,CPU的速度越快,反之亦然。
访问速度
- L1 高速缓存访问速度:4 个 CPU 时钟周期
- L2 高速缓存访问速度:11 个 CPU 时钟周期
- L3 高速缓存访问速度:39 个 CPU 时钟周期
- RAM 内存访问速度:107 个 CPU 时钟周期
我们可以看到L1的速度是RAM的27倍,L1和L2的访问大小基本是KB级别,L3是MB级别。例如 Intel Core i7–8700K,是一个 6 核 CPU,每个核上的 L1 为 64KB(数据和指令各 32KB),L2 为 256K,L3 为 2MB。
计算机内存层次图
它是用于计算机内存的金字塔形结构。现在缓存的概念已经扩展,不仅在处理器和主存之间有缓存,在内存和硬盘之间也有磁盘缓存,甚至在硬盘和网络之间有一定的缓存,也就是所谓的临时互联网。文件夹或网络内容缓存。任何位于两种速度差异较大的硬件之间,用于协调两种设备之间数据传输速度差异的结构都可以称为缓存。
- 硬件缓存:CPU缓存、GPU缓存、DSP
- 软件缓存:磁盘缓存、Web缓存等
不同的存储技术有不同的价格和性能权衡。访问速度越慢,内存的价格就越便宜。
局部引用原则
- 处理器在任何时候都只能访问一小组指令和数据
- 时间局部性(及时)
- 所有最近执行的数据和指令都有很高的再次执行机会。
2. 空间局部性(在空间中)
- 所有这些数据和指令都存储在最近执行的指令附近
但是,我们不可能在一级缓存中缓存所有的数据和指令,而只能缓存一部分数据和指令。这个时候,就需要利用当地的特性了。空间局部性会告诉我们相邻的数据经常被一起访问。因此,数据以块为单位(连续的数据)在层之间传输。如果一级缓存已经满了怎么办?最简单的方法是选择另一个块并随机插入。但这不一定是最好的方法。时间局部性告诉我们最近和最近访问的数据应该被缓存并且应该被替换。
缓存的一般概念
我们假设系统只有多级缓存。一般是先检查一级缓存,如果命中则处理器高速运行。如果一级缓存未命中,则在检查主存之前检查二级缓存,依此类推。缓存有2种,即inclusive cache(L1中的数据也可能在L2 cache中)和exclusive Cache(数据保证在L1和L2缓存中最多一个。独占缓存是在L1和L2缓存中没有可重复的数据,与包含缓存相比可以存储更多的数据。
处理器将地址发送到缓存
- Cache Hit - 缓存中地址的数据,并根据块的偏移量返回数据
- Cache Miss - 数据不在缓存中,从内存中获取数据,将其发送回处理器,并将此数据保留在缓存中,进行缓存行填充。处理器必须处理可变的内存访问时间
- 此外,我们需要考虑缓存延迟和命中率之间的权衡。基本上,更大的缓存具有更好的命中率,但延迟更长。因此,许多计算机使用多级缓存,小的快速缓存由较大的慢速缓存支持。
- 缓存未命中会增加延迟。
减少缓存未命中的方法
- 我们可以应用适当的缓存替换策略
- FIFO(先进先出)
- LIFO(后进先出)
- LRU(最近最少使用)
- MRU(最近使用)
地址的构成
要了解寄存器的工作原理,必须了解注册表是由什么组成的,以及它是如何与处理器通信的。一个地址由 2 个块组成:块地址和块偏移(选择数据块中的字节)。块地址由两部分组成:Tag(看是否命中了需要的数据) Index(选择一个组)。
访问方法
高速缓存分为许多高速缓存行。高速缓存行的大小从 16 字节到 128 字节不等。一般大小为 64 字节。所以有一个512KB的缓存,可以分为8192条缓存线。
那么地址访问如何映射到缓存呢?地址分为逻辑地址、线性地址和物理地址。
该图显示了从逻辑地址到线性地址再到物理地址的过程。从线性地址到物理地址的转换过程需要使用页表。页表由许多条目组成,每个条目称为页表条目,整个页表由操作系统维护并放置在内存或磁盘中。内存访问需要数百个时钟周期,每次地址转换都查看内存页表是浪费时间。因此,现代计算机引入了翻译后备缓冲区(TLB) 以加快进程。每次地址转换时 CPU 都会检查 TLB。如果只有一次地址转换,则无需获取内存页表。在 TLB 命中的情况下,可以选择物理地址。
缓存关联性
替换策略确定特定内存块进入高速缓存时可以放置的位置。要将块放入高速缓存,该集合由从内存块地址派生的索引位确定。标签存储在与该集合关联的标签字段中。
设置>索引位>标签
关联性是一种权衡。当高速缓存搜索命中时,必须搜索所有高速缓存行。它需要更多的功率、空间消耗和时间。另一方面,具有更多关联性的缓存遭受更少的未命中。将关联性加倍,从直接映射到 2 路,或从 2 路到 4 路,对命中率的影响与将缓存大小加倍大致相同。但是,超过 4 路的关联性增加对命中率的影响要小得多。
映射关系主要有 3 种类型。
- 直接映射——主内存中的每个块只能放置在缓存中的唯一位置。它的特点是空间利用率低,冲突概率最高,实现最简单。
- 全关联映射——主内存中的任何块都可以放在缓存中的任何位置。它的特点是空间利用率高、冲突概率最低、实现最复杂。
- 组关联映射——主内存中的每个块都可以放置在缓存中唯一组中的任何位置。组关联映射是直接映射和完全关联映射之间的折衷。
直接映射缓存
缓存的大小称为缓存大小,表示缓存可以放置的最大数据的大小。我们将缓存平均分成许多相等的块,每个块的大小称为一个缓存行,它的大小就是缓存行大小。每个缓存行对应一个唯一的标签,标签存储整个地址的剩余部分(位宽减去索引和偏移使用的位)。
标签、索引和偏移量的组合可以唯一地确定一个地址。每个地址可以直接且仅立即映射到某个缓存行。
例如,一个 64 字节的缓存被平均分为 64 个块,那么缓存行是 1 个字节,所以我们总共有 64 个缓存行。如果我们将 64 个字节平均分成 8 个块,那么缓存行是 8 个字节,因此每个块总共有 8 个缓存行。
一般高速缓存行大小为 4–128 字节。如果发生块冲突,将无条件替换缓存中的原始块。处理器会直接根据地址中的索引位找到对应的缓存块号,取出当前缓存块中所有缓存行对应的标签,然后与地址中的标签进行比较。如果块号的有效位为 1,则为命中,否则为未命中。(1 表示有效,0 表示无效)。缓存控制器可以根据有效位来确认当前缓存行数据是否有效。取出当前缓存块中所有缓存行对应的标签,然后与地址中的标签进行比较。如果块号的有效位为 1,则为命中,否则为未命中。(1 表示有效,0 表示无效)。缓存控制器可以根据有效位来确认当前缓存行数据是否有效。取出当前缓存块中所有缓存行对应的标签,然后与地址中的标签进行比较。如果块号的有效位为 1,则为命中,否则为未命中。(1 表示有效,0 表示无效)。缓存控制器可以根据有效位来确认当前缓存行数据是否有效。
缓存>缓存块>缓存行
全关联映射
此缓存是全连接缓存。所有高速缓存行都在一个组中,因此不需要在地址中设置索引。这是因为只有一组可供您选择。处理器根据地址的标签比较所有缓存行对应的标签。如果相等,则表示缓存命中。因此,在完全关联的缓存中,任何地址的数据都可以缓存在任何缓存行中。它可以最大限度地减少缓存抖动的频率,但硬件成本更高。显然,它提供了灵活性(缩短了块冲突)和高命中率。
组关联映射
将缓存分成大小相同的组,主存中的一个缓存块可以加载到组中的任意位置。
双向集关联缓存
假设缓存大小为 64 字节,缓存行大小为 8 字节。因此,双向组关联缓存将缓存平均分为 2 部分,每 32 个字节。每个部分包含 4 个缓存行。具有相同索引的所有缓存行将被组合在一起。处理器会直接根据地址中的索引位找到对应的缓存块号,取出当前缓存块中所有缓存行对应的标签,然后与地址中的标签进行比较。如果块号的有效位为 1,则为命中,否则为未命中。(1 表示有效,0 表示无效)。缓存控制器可以根据有效位来确认当前缓存块是否有效。双向组关联缓存的硬件成本高于直接映射缓存。这是因为它每次都比较标签,它需要比较多个缓存行对应的标签。一些计算机可能会进行并行比较以提高比较速度,这增加了硬件设计的复杂性。但是,它有助于减少缓存抖动的可能性。
四路集关联缓存
假设一个四路组相联缓存,即缓存中的每个位置存储4行数据,其数据大小为8 KB,每个缓存行大小为16字节。将有 128 个位置,因为 128 个位置 * 4 行/位置 * 16 字节/行 = 8 KB。该概念类似于 2 路组关联缓存。
寄存器有 4 个重要特征。
- Cache Placement (Put data in the buffer) — 直接映射、全关联映射和集合关联缓存映射
- 高速缓存搜索(在高速缓存中搜索数据) ——处理器使用地址 tp 中的标记位与高速缓存中的标记进行比较。如果匹配,则表示缓存命中。
- Cache Replacement(替换缓冲区中的数据) ——FIFO(先进先出)、LIFO(后进先出)、LRU(最近最少使用)、MRU(最近使用最多)
- Cache Write Policy(通过缓冲区向主存写入数据) —— Hit(Write-through、write-back)、Miss(write-allocate、no-write allocate)
缓存写策略
缓存更新——缓存更新策略是指当缓存命中发生时写入操作应该如何更新数据。
- 直写——当处理器执行存储指令并命中缓存时,我们更新缓存中的数据并更新主存中的数据。这样,缓存中的数据始终是一致的。
- 回写——当处理器执行存储指令并命中缓存时,我们只更新缓存中的数据。每个缓存行修改后的数据将存储在脏位(缓存行旁边的D)中。主存中的数据仅在缓存行被替换或执行清理操作时更新。因此,缓存中的数据和主存中的数据可能不一致。
缓存分配策略——它指的是我们应该何时为数据分配缓存行。它分为2个操作:读和写。
- Cache-Write Allocation — 仅在 CPU 写入缓存未命中的数据时才考虑。当我们不支持写分配时,写指令只是更新主存数据。当支持写分配时,我们首先将数据从主存加载到缓存行中,然后更新缓存行中的数据。
- Cache-no-write allocation/Read Allocation — 当 CPU 读取数据时,会发生缓存未命中,在这种情况下,会分配一个缓存行来缓存从主存储器读取的数据。
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。