跳转至

index

Cache基本工作原理

Cache的核心定位与基本工作原理

Cache(高速缓冲存储器)是CPU与主存之间的小容量高速缓冲存储层级,是现代计算机存储体系中弥补CPU与主存速度鸿沟的核心组件,核心特性与工作逻辑如下:

  1. 硬件基础:由高速静态随机访问存储器(SRAM) 构成,无需刷新操作,访问速度与CPU主频处于同一量级;现代CPU中Cache分为多级,L1/L2级Cache集成在CPU内核内部(核内Cache),L3级Cache为多核共享的片内Cache,整体访问延迟远低于由DRAM构成的主存(Main Memory, MM)。
  2. 核心理论基础:程序访问的局部性原理\ Cache的高效工作完全依赖程序访问的局部性特征,分为两个核心维度:
  3. 时间局部性:当前被访问的指令/数据,在短时间内大概率会被再次访问(如循环体、循环变量、高频调用的函数)
  4. 空间局部性:当前被访问的指令/数据,其相邻的存储单元大概率会被很快访问(如顺序执行的指令、数组的连续元素)
  5. 核心工作逻辑\ 程序运行时,CPU会基于局部性原理,将即将用到的、主存中相邻的一批数据/指令,预先成批拷贝到Cache中,Cache的内容是主存中部分内容的镜像(副本),因此不会增加系统的总存储容量。\ 绝大多数情况下,CPU可直接从高速Cache中获取指令和数据,无需访问慢速的主存,从而大幅降低CPU的平均访存延迟,提升整机执行效率。

Cache与主存的存储结构划分

为了实现Cache与主存之间的批量数据交换,Cache-主存层次会将主存和Cache的存储空间,划分为固定大小、长度完全相等的最小传输单元,定义如下:

  1. 主存块(Block):主存空间按固定大小划分的连续存储区域,是Cache与主存之间数据传输的最小单位。
  2. Cache行(Cache Line):Cache空间中与主存块大小完全相等的存储区域,用于存放一个完整的主存块,是Cache的最小存储单元。

⚠️ 术语澄清:主存块≠页/页框/页面。页(Page)是虚拟存储器(虚存-主存层次)的地址映射与数据传输单位,面向操作系统内存管理;而主存块是Cache-主存层次的传输单位,由纯硬件实现,二者分属存储层次的不同层级,不可混淆。

Cache行的核心标识位

每个Cache行除了存放主存块数据的存储区,还必须配套两个核心标识位,用于寻址和合法性判断,是Cache命中判断的核心依据。

有效位(Valid Bit)

  • 核心作用:标识当前Cache行中的数据是否合法有效。
  • 工作规则:系统上电/复位时,所有Cache行的有效位默认清0,标识行内数据无效;只有当Cache行被装入合法的主存块数据后,有效位才会被置1。
  • 应用:淘汰Cache行时,仅需将对应行的有效位清0,即可标记该行数据无效;装入新主存块时,再置1有效位完成更新。
  • 有效位为0时,无论该行其他标识位是什么,CPU访问时均直接判定为Cache缺失。

标记位(Tag)

  • 核心作用:标识当前Cache行中存放的主存块,对应主存空间的地址编号,是CPU判断「目标主存块是否在Cache中」的核心依据。
  • 工作规则:主存中每个主存块都有唯一的块地址,将该块地址的核心段存入Cache行的Tag位;CPU访存时,会将主存地址的Tag段与Cache行的Tag位进行比对,仅当Tag匹配且有效位为1时,才判定为Cache命中。

CPU访问Cache的执行流程

e248b4723c58d357b334b3016c263a9a.svg

说明:

  1. 上述整个访存过程——判断是否命中、从 Cache 取数、从主存调块、替换 Cache 行——都必须在一条指令的执行周期内完成,因此只能由硬件实现,对程序员完全透明(程序员编程时无需感知 Cache 的存在,因此Cache缺失不会触发CPU陷入内核,不会引起进程切换。)
  2. Cache-主存体系中,CPU 与主存之间建立有直接访问通道。缺失时,CPU 直接访问主存获取所需数据,并同时将对应主存块调入 Cache(后台执行),从而减少 CPU 等待时间。
  3. 缺失时的访问时间 = 主存访问时间 \(T_m\) + Cache 访问时间 \(T_c\)。

某些 CPU 采用同时访问 Cache 与主存的策略——若 Cache 命中则立即中止主存访问。此策略下,缺失时的访问时间即为一次主存访问时间 \(T_m\)。一般不会这样考,

现代 CPU 也不会这样设计。

Cache-主存层次的性能指标

Cache的核心价值是缩短CPU的平均访存时间,核心性能指标定义与计算如下:

  • 命中(Hit):CPU访问的目标主存单元所在的主存块,已合法存放在Cache中,可直接从Cache读取数据。
  • 命中率p:Cache命中的次数与CPU访存总次数的比值,取值范围0≤p≤1
  • 缺失(Miss):CPU访问的目标主存单元所在的主存块,不在Cache中,或Cache行有效位为0,必须访问主存。
  • 缺失率(1-p):Cache缺失的次数与CPU访存总次数的比值,与命中率完全互补。
  • 命中时间Tc:Cache命中时,CPU完成一次访存的时间,即Cache的访问延迟。
  • 主存访问时间Tm:CPU完成一次主存访问的延迟,通常Tm是Tc的几十到上百倍。

平均访问时间公式

  1. 串行访问模型(先查 Cache,未中再访主存):\(T_a = p \times T_c + (1-p) \times (T_c + T_m) = T_c + (1-p) \times T_m\)
  2. 并行访问模型(同时访问 Cache 与主存,命中则立即取消主存请求):\(T_a = p \times T_c + (1-p) \times T_m\)

Note

要实现完整的Cache存储体系,必须解决以下四个核心问题,也是Cache技术的核心学习脉络:

  1. 地址映射与变换问题:主存块按照什么规则存放到Cache行中?CPU如何通过主存地址,快速定位到对应的Cache行?(对应直接映射、全相联映射、组相联映射三大映射方式)
  2. 块替换算法问题:当Cache已满、无空闲行时,应该淘汰哪一个Cache行,才能最大化长期命中率?(对应LRU、FIFO、随机替换、LFU等经典替换算法)
  3. 写操作一致性问题:CPU执行写操作时,如何保证Cache中的数据与主存中的数据一致?(对应写直达、写回、写分配、非写分配等写策略)
  4. 地址翻译:如何将物理地址拆解为 Tag | Index | Offset 以实现硬件快速匹配?

cache和主存块之间的映射方式

Cache 的行数远少于主存的块数,这就带来了一个根本性问题:主存中的哪个块,应该放到 Cache 的哪一行? 这就是 Cache 映射方式所要解决的核心问题。

不同的映射方式在以下三个维度上进行权衡:

  • 命中率:一个主存块的可选位置越多,灵活性越高,Cache 利用率和命中率越高
  • 查找速度:可选位置越多,查找时需要比较的 Tag 越多,硬件开销越大
  • 冲突率:多个主存块争抢同一 Cache 位置,导致频繁替换,命中率下降

围绕这三个维度的不同取舍,产生了三种经典的映射方式:直接映射、全相联映射和组相联映射。

(1)直接相联:各主存块只能映射到 cache 中的固定块。

(2)全相联:各主存块都可以映射到 cache 的任意数据块。

(3)组相联:各主存块只能映射到 cache 固定组中的任意块。


直接映射方式

基本思想(原理)

把主存的每一块映射到 固定的 一个cache 行中,也称模映射,其映射关系为:

Note

Cache行号=主存块号 mod Cache行数

以上规则等效于将主存按照cache大小进行分区,每个分区中包含的块数与cache的行数相同,因此主存地址可细分为区地址(标记 tag)、区内行索引(index)、块内偏移(offset 即块内地址)三部分,这里index字段就是数据块映射到cache中的行号,和上式中的余数完全相同。各字段划分及位宽如图所示。

1777274114251-38d470f5-0045-4c5c-b638-670f31cf90f4.png

如图, cache块大小= \(2^w\)字节,cache行数\(n = 2^r\),主存容量\(= 2^{s+w}\)字节,如果不考虑脏数据位和淘汰计数,cache的实际容量为\(n×( 1 + s-r + 8×2^w )\)位。

由于主存块只能放置在index对应的cache行中,因此直接相联映射并不需要全相联查找,查找表不需要存放在相联存储器中,各cache行中的元数据信息(tag, valid, dirty)只需要和数据块副本存放在一起即可。查找时可以通过index字段快速访问对应cache行的标记与标志字段,如果标记字段有效且与主存地址中的区地址相同,就表示命中。与全相联相比,直接相联映射只需要设置 一个共享的比较器 即可完成查找,硬件成本更低。

直接映射的硬件实现

以袁书的该示例讲解,假定主存和 cache 之间采用直接映射方式,块大小为 16B。cache 的数据区容量为 64KB,主存地址为 32 位,按字节编址。问:主存地址如何划分?说明访存过程,并计算 cache 总容量为多少。

1777442073487-473f1c51-375a-4142-a36b-8f418275735b.png

解:cache 数据区容量为 \(64\text{KB} = 2^{16}\text{B} = 2^{12}\text{行} \times 2^4\text{B/行}\)。

因为主存的每\(2^{12}\) 块和 cache 的 \(2^{12}\)行一一对应,所以可将主存的每个 \(2^{12}\) 块看成一个块群。主存地址位数 n=32,主存地址空间划分为 \(2^{32}\text{B} = 2^{28}\text{块} \times 2^4\text{B/块} = 2^{16}\text{块群} \times 2^{12}\text{块/块群} \times 2^4\text{B/块}\)。因此,标记位数 t=16,行号位数 c=12,块内地址位数为 4。

主存地址的划分以及访存过程实现如图所示。图中 tag 表示标记字段;index 表示 cache 行索引,即行号;块内地址分为两部分:高两位(word 字段)为字偏移量、低两位(byte 字段)为字节偏移量。hit 表示命中。

整个访存过程由硬件实现,分为图中 5 个步骤。CPU 在 hit 为 1 的情况下,根据要访问的是字还是字节选择从第④步还是第⑤步得到结果。若 hit 不为 1,则 CPU 要启动一次“cache 行读”总线事务操作,通过总线到主存读一块连续的信息到 cache 行中。

从图中可看出,每个 cache 行由一位有效位 V、16 位标记(tag)和 4 个 32 位的数据(data)组成,共有 \(2^{12} = 4\text{K}\) 行,因此,cache 的总容量为 \(2^{12} \times (4 \times 32 + 16 + 1) = 4\text{K} \times 145 = 580\text{Kbits} = 72.5\text{KB}\)。其中,数据占总容量的 \(64\text{KB} / 72.5\text{KB} = 88.3\%\)。

以谭志虎书的例子讲解(原理图风格不同,跟湖科大的可以看该示例)

题图

假设 cache 块大小为 4W(4 个字),共 8 行,主存按字访问,主存地址长度为 9 位。主存地址被划分为 tag(4 位)、index(3 位)、offset(2 位)三个部分。

  1. 行索引 index 字段(cache 行号)送入行索引译码器(3-8 译码器)得到8个行译码输出公式,其中输出为高电平的那一行就是指定的cache行;
  2. 行译码输出信号为 1 的那行对应的三态门打开,对应 cache 行的有效位+tag 结果得以输出并且作为比较器的一个输入,主存地址字段中的 tag +1 作为比较器的另一个输入, 如果比较得到相同,则 hit 为 1,表示 cache 访问命中;
  3. 顺便用这8路译码输出信号控制cache块数据的输出,将所有cache行的数据块都通过三态门输出到数据总线上,这些三态门的控制端分别是这8个译码信号,命中时,命中行的数据会输出到数据总线,具体选择数据块中的哪一个字由主存地址中的offset字段进行选择,这里可以利用一个多路选择器进行选择,注意命中信号 hit 直接连接多路选择器的使能端,缺失时输出高阻态。
  • Cache读命中时间最短,现代cpu中取指令取操作数都是从cache得到,通常在一个时钟周期内完成,所以cache性能直接决定了cpu性能。
  • 我们需要知道这里面的三态门、译码器、多路选择器、比较器(直接映射仅需 1 个)等元器件的作用,这些属于组合逻辑元件,具体介绍可以看 CPU 章节。

假设 cache 的初始状态为空,主存字地址读访问序列如下。根据直接映射主存地址结构,该访问序列对应的地址解析(tag,index,offset)以及访问情况如下表所示。

题图

第 1 次主存访问地址为 1F,其行索引字段 index=7,由于 cache 初始状态为空,第 7 行中有效位 valid = 0,访问缺失,载入 1F 所在的主存数据块(1C ~ 1F)到 cache 的第 7 行;同时将主存区地址 0 作为标记存放在第 7 行的标记字段中,并将有效位 valid 置 1,方便后续查找。

根据直接相联映射规则,第 2 ~ 5 次访问均为缺失状态,分别将对应主存数据块载入第 0、1、2、5 行中。

第 6 次访问的主存字地址为 55,行索引 index = 5,第 5 行的标记字段与主存地址标记字段相同,且 valid = 1,访问命中,由块内偏移 offset = 1 选择 cache 行中的 W1 输出。

第 7 次访问的主存地址为 103,行索引 index = 0,而第 0 行的有效位 valid = 1,表明该行已经包含数据,但标记字段 tag = 1,和当前地址的 tag 字段不相符,数据缺失。需要载入 103 所在的主存块到第 0 行中,并将原有数据替换。

显然相同的访问序列,直接相联映射在 cache 还存在空行的情况下就发生了冲突,所以其 cache 利用率不高。

数据写入时,可以将待写入数据加载到所有存储器的输入端,由 index 字段译码生成的行选通信号 L0 ~ L7 和 offset 精准控制对应存储单元的写使能信号,实现对有效位、标记字段 tag、数据缓冲区的写入。直接相联映射的替换算法较为简单,如果访问不命中则直接替换行索引译码器选中的 cache 行即可;但如果选中行存在脏数据,则需要将脏数据写入二级存储器以保证数据一致。

Tip

综上所述,直接相联映射方式具有以下特点:

(1)由于主存数据块只能映射到cache中的特定行,因此cache利用率低,命中率低。

(2)index相同的所有主存块映射到cache中的同一行,cache未满也可能发生数据冲突,cache的冲突率高。

(3)查找时只需根据index字段访问对应cache行的标记字段tag并进行比较,只需一个比较器,硬件成本较低,适合于大容量cache使用。

(4)无须使用复杂的替换算法,直接替换冲突数据块即可。

全相联映射方式

基本思想(原理)

一个主存块可装入cache任意一行中。全相联映射cache中,每行的标记用于指出该行取自主存的哪个块。因为一个主存块可能在任意一个cache行中,所以,需要比较所有cache行的标记,因此,主存地址中不需要cache行索引,只有标记和块内地址两个字段。全相联映射方式下,新的主存数据块可以载入cache中任何一个空位置(利用率最高,但查找成本较高),只有cache满时才需要进行cache 行置换。

1777277674730-f168d06c-6fc5-4638-ab60-e506c02cb034.png

由于主存块可以放置在cache的任意块中,为了方便后续查找,主存数据块载入时还需要记录若干的标记标志信息,主要包括有效位、主存块地址标记、脏数据标志位、淘汰计数(替换算法控制位)等信息。

主存地址划分为主存块地址(tag)和块内偏移两部分,两部分字段长度分别为\(s、w\)。由图可知, cache 数据块大小\(=2^ w\)字节,cache 副本缓冲区容量\(= n×2^w\)字节,主存容量 \(= 2^{s+w}\)字节,不考虑脏数据位和淘汰计数,cache 的实际容量为\(n×( 1 + s + 8×2^w )\)位。

数据查找时直接将主存块地址和所有 cache 行中的标记字段主存块地址进行并发比较,如果 读命中,则输出对应 cache 行数据块副本中块内地址 offset 处的数据;如果写命中,则需要写入数据,同时将脏数据标志位置为 1。


全相联映射的硬件实现

1777441976107-1d92ed47-baf3-4751-80ee-dc3fa7f25ee6.png

在全相联映射下,主存中的任意一块可以放在 Cache 的任意一行。这就导致了 CPU 发出一个主存地址后,Cache 必须“漫天撒网”去寻找。具体流程如下:

1. 地址拆分(主存地址输入):主存地址被切分为两部分:主存字块标记(Tag) 和 块内地址(Offset)。

2. 并行比较(图中的“比较器”与“与门”):这是全相联映射硬件开销最大的一步。主存地址中的 Tag 被同时广播(送入)到 所有 Cache 行的比较器

  • cache 的每一行都有一个比较器。它会将当前行存储的 Tag 与 CPU 送来的 Tag 进行比对。
  • 有效位校验: 仅仅 Tag 相等还不够,这一行的数据必须是有效的(没有被标记为脏数据或空数据)。因此,比较器的输出结果还要和这一行的有效位 V 一起送入与门(图中绿色的逻辑部件)。
  • 结论: 只有当 Tag 相等 且 V = 1 时,该与门才会输出 1,表示“这一行命中了”。

3. 生成全局命中信号(图中的“或门”):Cache 需要告诉 CPU 到底有没有找到数据。

  • 所有 Cache 行的与门输出信号,全部送到这个紫色的或门(全 0 出 0,有 1 出 1)中。
  • 只要这若干行中有任何一行命中了(输出 1),或门就会输出 1,生成最终的全局 hit(是否命中)信号。如果全部没命中,或门输出 0,触发 Cache Miss,CPU 转而访问主存。

4. 数据输出:命中后,哪一行的数据输出到总线?

每行数据区---> 三态门 ---> MUX输入端
                ↑
            与门输出(OE使能端)
  • 只有命中行的与门输出为1,其三态门被打开,该行数据进入 MUX
  • 其余行三态门高阻,不影响总线
  • MUX 接收有效数据后,再根据块内地址从该块中选出对应的字/字节输出

对应图中右侧的流程:

三态门输出 → MUX → (块内地址选择)→ 字/字节数据输出
  1. 缺失处理(图中未画,补充说明)当 hit = 0 时:

  2. 从主存调入该块

  3. 在 Cache 中选择一个空闲行或 牺牲行(需要替换算法:LRU、FIFO、随机等)

  4. 将新块写入该行,更新 Tag,置 V=1

  5. 重新执行命中流程

综上所述,全相联映射方式具有以下特点。

(1)主存数据块可以映射到 cache 的任意一行,因此cache 利用率高。

(2)只要 cache 中还有空行就不会引起冲突,因此 cache 的冲突率低。

(3)查找时需要并发比较查找表中所有项,每一个 cache 行对应一个比较电路,硬件成本较高,只适合于小容量 cache 使用。

(4)cache 满时载入新数据块需要利用替换算法进行替换,替换策略和算法较为复杂。

组相联映射

基本思想(原理)

全相联映射命中率高,但查找硬件成本高;而直接相联映射查找成本低,但命中率低,二者在特性上正好互补。组相联映射是直接相联映射和全相联映射两种方式的折中,既能提高命中率,又能降低查找硬件的开销。

组相联映射将 cache 分成固定大小的组,每组有 k 行,称为 k-路组相联;主存数据块首先采用直接相联映射的方式定位到 cache 中固定的组,然后采用全相联映射的方式映射到组内任何一个 cache 行,组相联映射规则如下:

\(\text{cache 组号} = \text{主存块号} \mod (\text{cache 组数})\)

根据该规则,主存地址可细分为标记字段 (tag)、组索引 (index)、块内偏移 (offset) 3 部分,这里的组索引实际就是上式中的余数。

直接相联映射让数据查找的范围快速缩小到某一个 cache 组,大大缩小了查找范围,降低了全相联并发比较的硬件开销;cache 组内多个 cache 行则采用全相联映射规则,有效避免了直接相联映射冲突率较高的问题,大大提高了 cache 命中率。

下图所示为一个二路组相联的逻辑示意图,cache 一共包括 n 组,同样也可以将主存地址中的主存块地址细分为标记字段 tag 和组索引 index 两部分。由图可知,cache 块大小 = \(2^w\) 字节,组数 \(n = 2^d\),主存容量 = \(2^{s+w}\) 字节,cache 容量 = \(2n \times (1 + s - d + 8 \times 2^w)\) 位。

1777279831658-2a788d28-3f05-40e3-a90c-ff3d3cb24af3.png

图中 B₀ 块的组索引字段为 0,所以只能映射到第 0 组。同理,B₁ ~ Bₙ₋₁ 块只能分别映射到第 1 ~ n-1 组,而 Bₙ ~ B₂ₙ₋₁ 也分别映射到第 0 ~ n-1 组。

组相联进行数据查找时,首先利用主存块地址中的组索引字段 index 定位具体 cache 组,然后将标记字段与组内所有行的标记以及有效位进行全相联并发比较,如有相符的,则表示命中,否则表示数据缺失。注意这里全相联比较只局限于组内,所以全相联比较所需要的比较器为 k 个,大大降低了硬件开销。

组相联映射的硬件实现

1777441893867-98b7fa7e-331c-408f-9587-2319bce46f8c.png

以上图为例来对组相联映射访存流程进行详解

图示为 2路组相联:Cache 被分为若干"组" 每组内有 2 条"路",即 2 行\ 主存块 → 固定映射到某一组,但可放入该组任意一路

  1. 第一阶段:地址拆分:当 CPU 发出访存主存地址时,地址被严格切分为三段:标记(Tag)、Cache 组号(Index) 和 块内地址(Offset)。 硬件首先提取出中间的 cache组号。这个组号就像直接映射中的行号一样,经过译码器(图中省略)快速定位到 Cache 中的唯一一个“组”

这一步的精髓在于:我们不需要像全相联那样去 cache 的所有行找了,只需要去这一特定的“组”里找。

组号字段的位数决定组数,与路数无关。若有\(2^s\)组,则组号占 s 位。

  1. 第二阶段:组内并行比较 tag:定位到具体的组之后,就轮到全相联的机制登场了。在这个组内,包含了 2 个“路”(即两个 Cache 行)。
  2. 主存地址中的 Tag 被同时送到这一组的左右两个比较器中,与组内两个 Cache 行存的 Tag 进行并行比较。
  3. 仅仅 Tag 相同还不够,必须确保数据是有效的。因此比较器的结果会和各自行的有效位 V 送入与门。只有 Tag 相等 且 V=1,对应的与门才会输出 1,表示“该路命中”。
  4. 第三阶段:生成全局命中信号以及数据输出
  5. 全局 Hit 信号: 左右两个与门的输出结果,共同连接到一个或门(全 0 出 0,有 1 出 1)。只要这 2 路中有一路命中了,或门就会输出 1,向 CPU 发出最终的全局 Hit 信号。
  6. 数据输出: 三态门的作用: 哪个与门输出了 1(代表命中),它就会打开自己下方的橙色三态门,将命中那一行的整块数据(Data)释放到数据总线上;未命中的那一路三态门保持关闭,防止数据冲突。
    • MUX 的精细挑选: 此时,整块数据来到了底部的多路选择器(MUX)。主存地址的最后一部分 块内地址 作为控制信号,指挥 MUX 从这一大块数据中精准截取出 CPU 想要的那个字或字节。
    • OE 使能端保障: MUX 的输出受到全局 Hit 信号的控制(连到了 MUX 的 OE 端)。只有真的命中了,挑好的数据才会最终输出给 CPU;如果 Hit=0(Cache 缺失),则直接阻断输出,CPU 乖乖去访问主存。

组相联映射与全相联映射相比,其每一组的多路比较器数目大幅减少,且为各 cache 组共享, 硬件成本更低。当每组只有一个 cache 行时,也就是只有 1 路组相联,此时只需要一个比较器, 电路演变成直接相联映射。当整个 cache 只有一组时,无须组索引译码器,电路演变成全相联映射。

e91f8e72e1d8fb0662ecb98a55e8a951.svg

三种映射方式的对比总结

  1. 地址结构对比:三种映射方式最直观的区别体现在主存地址的划分方式上:

1777359581745-60a0c703-69d0-4a51-8866-8480dad590ac.png

全相联映射的 Tag 字段最长(因为没有行号/组号字段,Tag 必须覆盖更多地址信息);直接映射的 Tag 最短;组相联介于两者之间。

  1. 映射规则对比
直接映射 全相联映射 组相联映射(k路)
映射规则 Cache行号 = 主存块号 mod Cache行数 主存块可放入 任意 Cache行 Cache组号 = 主存块号 mod Cache组数;组内任意行
一个主存块的可放位置 唯一确定(1个) 任意(n个,n为Cache总行数) 组内任意(k个)
本质 全相联的特例(k=1,整体只有n组) 组相联的特例(k=n,整体只有1组) 直接映射与全相联的折中
  1. 硬件结构对比
直接映射 全相联映射 组相联映射(k路)
比较器数量 1个(共享) n个(每行一个,并行比较) k个(每组k个,仅在组内并行)
是否需要译码器 需要(行号译码) 不需要 需要(组号译码)
查找方式 索引定位→1路比较 n路并行比较 索引定位组→组内k路并行比较
硬件成本 最低 最高 居中
适用容量 大容量Cache 小容量Cache(如TLB) 最常用(L1/L2/L3均采用)
  1. 性能指标对比
直接映射 全相联映射 组相联映射
Cache利用率 低 最高 较高
命中率 低 最高 较高
冲突率 最高(Cache未满也可能冲突) 最低(仅Cache全满时才冲突) 居中
替换算法 无需(冲突时直接替换) 需要复杂替换算法 需要替换算法(仅组内)

冲突率的本质:直接映射中,所有 Cache行号相同的主存块(即同一"列"的块)互相竞争同一行,Cache 中有空行也可能发生冲突;全相联中,只有整个 Cache 全满时才会发生替换,冲突率极低。

组相联映射可以统一描述另外两种映射:

k路组相联,共m组
├── 当 k=1(每组1行,共m组) → 退化为直接映射
│     只有1个比较器,电路等价于直接映射
└── 当 m=1(只有1组,共k=n行) → 退化为全相联映射
      无需组号译码器,电路等价于全相联映射

这也解释了为什么现代 CPU 的 Cache 普遍采用 4路、8路、16路组相联:路数越多,命中率越接近全相联,但硬件成本仍可控;而路数为1退化到直接映射则冲突率过高,路数等于总行数退化到全相联则硬件代价过大。

追求硬件成本最低,容量大? → 直接映射\ 追求命中率最高,容量小? → 全相联映射(如TLB)\ 追求性能与成本的最佳平衡? → 组相联映射(现代CPU Cache的主流选择)

1777361409749-32f3bd7c-7b83-46be-afd1-c7ea87b29a2a.png

当cache大小、主存块大小一定时,关联度和命中率、命中时间、标记所占额外开销等有如下关系:

  • 关联度越低,命中率越低。因此直接映射命中率最低,全相联映射命中率最高。
  • 关联度越低,判断是否命中的开销越小,命中时间越短。因此,直接映射的命中时间最短,全相联映射的命中时间最长。
  • 关联度越低,标记所占额外空间开销越少。因此,直接映射额外空间开销最少,全相联映射额外空间开销最大。

Cache中主存块的替换算法

cache行数比主存块数少得多,因此,往往多个主存块会映射到同一个cache行中。当新的一个主存块复制到cache时,cache中的对应行可能已经全部被占满,此时,必须选择淘汰掉一个cache行中的主存块。那调出哪一块呢?这就是淘汰策略问题,也称为替换算法或替换策略。\ 常用的替换算法有:先进先出(First-In-First-out,简称FIFO)、最近最少用(Least-Recently Used,简称LRU)、最不经常用(Least-Frequently Used,简称LFU)和随机替换等。可以根据实现的难易程度以及是否能获得较高的命中率这两方面来决定采用哪种算法。

现在我们通过下面这个例子讲解这几种算法,假设我们有一个 4路组相联 的 Cache(意味着每个组内有 4 个空位 / Cache 行)。现在关注其中的某一个特定的组(比如第 0 组),它目前的 4 个空位都是空的。接下来,程序将依次访问映射到该组的主存块,访问序列(块号)如下:

Tip

1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5

前 4 次访问 (1, 2, 3, 4) 会直接填满这 4 个空位。此时 Cache 第 0 组的状态为:[1, 2, 3, 4]。接下来访问 1 和 2 都会命中。当访问到数字 5 时,冲突发生了:Cache 满了,必须淘汰一个旧的。淘汰谁呢?

先进先出算法(FIFO:First-In-First-Out)

核心思想: 谁先进入 Cache,谁就最先被淘汰。这是一种“认老不认新”的策略。

实现机制: 可以把 Cache 组想象成一个排队的队伍,或者维护一个循环指针。新来的放队尾,满的时候把队头的人赶走。它不考虑数据被访问的频率。\ 当前 Cache:[1, 2, 3, 4] (1最先进入,4最后进入)\ 此时访问 5:

  • 按照 FIFO 原则,最先进入的是 1,所以淘汰 1,换入 5。
  • Cache 状态变为:[5, 2, 3, 4]

优缺点:

  • 优点: 硬件实现非常简单,只需要一个计数器或循环队列即可。
  • 缺点: 性能较差。有些数据可能很早就被加载进来了,但它被极度频繁地使用(比如循环控制变量、全局常数),FIFO 也会无情地把它淘汰,导致频繁的抖动(Belady异常)。(即不能正确反映程序的访问局部性,这种算法有可能产生较大的缺失率)

最近最少用算法(LRU:Least-Recently Used)

核心思想: 预测未来。如果一个数据在最近一段时间内没有被访问过,那么它在将来被访问的可能性也很小。因此,淘汰最久没有被访问过的那个块。

实现机制: 需要给每个 Cache 行增加一个计数器(或时间戳)。每次命中,将命中的行清零,其他所有行加一;每次替换时,找到计数器值最大(即最久未访问)的那一行替换。

Tip

这个计数器的值称为LRU位,其位数与cache组大小有关。2路组相联时有1位LRU位,4路组相联时有2位LRU位,即每块的LRU替换位=log2(组内块数)bit。

回看序列:1, 2, 3, 4, 1, 2, 5\ 当前 Cache:[1, 2, 3, 4]\ 在访问 5 之前,最近的访问历史是:... 3, 4, 1, 2。

  • 此时看 Cache 中的块:
  • 2 是刚才刚刚被访问的(最新)。
  • 1 是倒数第二次被访问的。
  • 4 是倒数第四次被访问的。
  • 3 是倒数第五次被访问的(最久远)。
  • 按照 LRU 原则,淘汰最久未访问的 3。
  • Cache 状态变为:[1, 2, 5, 4]

优缺点:

  • 优点: 最符合程序局部性原理(时间局部性),实际命中率很高,是目前主流 Cache 广泛采用的策略。
  • 缺点: 硬件开销大。需要为每个 Cache 行维护计数器,并且每次访问都要更新计数器,逻辑复杂。

ec1de213b99d37f5deb420c758767e34.svg

最不经常用算法(LFU:Least-Frequently Used)

核心思想: 预测未来。如果一个数据在过去一段时间内被访问的次数最少,那么它在将来被访问的可能性也很小。重点在于“频率/次数”。

实现机制: 为每个 Cache 行设置一个访问计数器。每次该行被访问命中,计数器加 1。发生缺失需要替换时,比较组内所有行的计数器,淘汰计数值最小的那个。\ 序列:1, 2, 3, 4, 1, 2, 5\ 当前 Cache:[1, 2, 3, 4]

  • 我们来统计在访问 5 之前,各个块的访问总次数:
  • 1 访问了 2 次。
  • 2 访问了 2 次。
  • 3 访问了 1 次。
  • 4 访问了 1 次。
  • 按照 LFU 原则,要淘汰访问次数最少的。此时 3 和 4 都是 1 次,发生了并列。
  • 遇到并列时,通常会结合 FIFO(淘汰最早进入的 3)或随机策略来打破僵局。假设采用 LFU+FIFO,淘汰 3。

优缺点:

  • 优点: 在某些特定负载下表现较好,体现了长期的高频热点数据。
  • 缺点: 极易受到“历史包袱”的影响。比如一个程序初始化阶段疯狂访问块 A 上千次,然后就不再用了,但由于它历史积分太高,LFU 会一直把它留在 Cache 中不肯淘汰,导致后面的有用数据进不来。硬件实现成本也很高。

随机替换算法

核心思想: 完全随机挑一个幸运儿淘汰。

实现机制: 硬件上挂一个随机数发生器,需要替换时摇个号,摇到几就淘汰对应的 Cache 行。

例子推演:\ 当前 Cache:[1, 2, 3, 4]\ 此时访问 5:

  • 闭着眼睛抓阄,假设随机数生成器输出了 2(代表数组下标或行号),就把原本位置上的块(可能是 2 也可能是 3,取决于具体存储位置)淘汰。

优缺点:

  • 优点: 硬件实现最简单,开销极低。速度极快,不需要比较或计算。
  • 缺点: 完全没有利用程序的局部性原理,命中率不稳定。但在某些超大容量、高相联度的特定场景(或作为复杂算法的后备补充)中,表现意外地还不算太差。

Tip

替换算法与 cache 的组织方式紧密相关。

  • 对采用直接相联映射方式的 cache 来说,因一个主存块只有一个特定的行位置可存放,所以不需要使用任何替换算法,只要有新的数据块调入,此特定行位置上的原数据一定会被换出。
  • 对全相联的 cache 而言,执行替换算法时,涉及全部 cache 行;
  • 而对组相联的 cache 来说,执行替换算法只涉及特定组中的行。

读操作完整流程(组相联 + 4种替换策略 + 脏块处理)

2c23e7a0122b1f4e63a748b8fe0e28d3.svg


cache的一致性问题

为什么会有一致性问题?

Cache 中存放的是主存数据的副本。当 CPU 执行写操作时,如果只更新了 Cache 中的副本而没有同步更新主存,就会导致 Cache 与主存中的数据不一致。

这种不一致在以下场景中会引发严重错误:

  • DMA 设备直接读写主存时,读到的是旧数据
  • 多核 CPU 中另一个核读取同一地址,得到的是过期值
  • 程序崩溃后主存中保存的是未更新的错误状态

解决一致性问题的关键,在于制定合理的写策略。写操作分两种情况处理:写命中和写缺失。

写命中时的策略

写命中(Write Hit):CPU 要写的地址恰好在 Cache 中,此时有两种处理方式。

写直通法(Write-Through)

又称:全写法、写穿法

核心思想:写操作同时更新 Cache 和主存,任何时刻两者保持一致。

写缓冲(Write Buffer)优化:为缓解写直通法频繁访问主存的性能问题,通常配置一个写缓冲队列(可以理解为一个数据缓冲寄存器)。CPU 将数据写入 Cache 后,同时把写请求放入写缓冲,由写缓冲异步地将数据更新到主存,CPU 无需等待主存写完即可继续执行。

bee1ae6dc40acd129ae9a1224ae05014.svg

特点分析:

维度 说明
一致性 始终一致,任何时刻主存都是最新值
写速度 慢,每次写操作都要访问慢速主存
实现复杂度 简单,无需脏位(dirty bit)
总线带宽 占用较多(每次写都产生主存访问)

写回法(Write-Back)

又称:回写法

核心思想:写操作只更新 Cache,不立即写主存;只有当该 Cache 行被替换时,才将修改过的数据写回主存。

296ef06c8d78447ca959ad00e05d10c5.svg

关键机制: 脏位 (Dirty Bit)

必须为每个 Cache 行设置 1 位脏位,标记该行与主存的一致性状态:

  • 脏位 = 1:该行数据被 CPU 修改过,与主存不一致,淘汰时必须先写回主存才能覆盖;
  • 脏位 = 0:该行数据与主存完全一致,淘汰时可直接覆盖,无需额外操作,节省主存带宽。。

特点分析:

维度 说明
一致性 不能保证实时一致,存在"脏数据窗口"
写速度 快,绝大多数写操作只访问 Cache
实现复杂度 较复杂,需要维护 dirty bit
总线带宽 占用少(只有替换脏块时才写主存)

两种写命中策略对比

对比维度 写直通法 写回法
Cache 与主存一致性 实时一致 存在不一致窗口
每次写操作主存是否访问 必须访问 通常不访问
是否需要脏位 不需要 需要
适用场景 对一致性要求高(如多核共享) 对性能要求高(主流 CPU)
是否可配写缓冲 可以(缓解写慢问题) 不需要

现代 CPU 普遍采用写回法:写直通法每次写操作都访问主存,频繁写主存会严重拖慢性能。

写回法将大量写操作限制在 Cache 内,只在替换时才写回,大幅减少主存访问。

写缺失时的策略

写缺失(Write Miss):CPU 要写的地址不在 Cache 中,此时同样有两种处理方式。

写分配法(Write-Allocate)

核心思想:写缺失时,先将目标块从主存调入 Cache,再在 Cache 中执行写操作。

33e2cc41f680305fdc555837ee87a74b.svg

设计动机:基于时间局部性——既然这个地址被写了,接下来很可能还会被读或再次被写,提前调入 Cache 可以让后续访问直接命中。

通常搭配:写回法。

为什么写分配法采取的是先根据地址把包含该数据的主存块整体调入 Cache 中,然后再修改 Cache 中的对应数据。而不是先把数据写进主存再调入 Cache。

写分配法的核心思想是既然这个地址即将被写,说明它很可能接下来还会被读或再次被写(时间局部性)。因此先把包含该目标数据的整个主存块(Block)调入 Cache,然后CPU 将想要写入的新数据,直接写到刚刚调入 Cache 的那个块的对应位置上。后续对该块的读写都在 Cache 中完成,避免反复访问慢速主存。

如果是"先写主存再调入",逻辑上等价但多了一次无意义的主存写操作(主存写操作很耗时间),实际不这样做。

CSAPP 等经典计算机黑书都是这种做法,现代 CPU 也是采取这样的设计思路。

非写分配法(No Write-Allocate)

核心思想:写缺失时,直接写主存,不将该块调入 Cache。

33ed81aa4bc8c770d6593adb84b77a49.svg

设计动机:如果写完之后短期内不会再访问该地址,把块调入 Cache 反而浪费空间,还会挤占其他更有价值的块。

通常搭配:写直通法。

两种写缺失策略对比

对比维度 写分配法 非写分配法
写缺失时的处理 调块入 Cache,再写 Cache 直接写主存,不动 Cache
后续读命中率 更高(块已在 Cache 中) 较低(块不在 Cache)
Cache 空间利用 可能引入不常用的块 不引入新块,节省 Cache 空间
通常搭配 写回法 写直通法

策略组合

实际系统中,写命中策略和写缺失策略成对使用:

b4f3633da0c5939133ce2510f5c44231.svg

这样搭配使用的原因
  1. 写回法 + 写分配法

写回法的目标:尽量把写操作留在 Cache 里,延迟写主存。

既然写回法已经决定"写操作在 Cache 中完成",那么写缺失时自然应该先把块调入 Cache,否则块不在 Cache 里,写回法就无从发挥——写完还是得立刻写主存,与写回法的设计目标矛盾。

写回法的逻辑链:写操作尽量在Cache完成
→ 写命中:直接改Cache ✓
→ 写缺失:先调块进来,再改Cache ✓(写分配)
→ 写缺失:跳过Cache直接写主存 ✗(与目标背道而驰)

  1. 写直通法 + 非写分配法

写直通法的目标:每次写操作都同步更新主存,保证一致性。

写直通法已经保证每次写都会到达主存,那么写缺失时把块调入 Cache 的意义就很小:

    • 调块进来 → 在 Cache 里写 → 同时还要写主存(写直通要求)
    • 不调块 → 直接写主存

两条路最终都要写主存,但前者多了一次"从主存把块搬进 Cache"的开销,还白白占用了一个 Cache 行。除非该地址后续会被频繁读取,否则这次调块纯属浪费。

写直通法的逻辑链:每次写都必须写主存
→ 写缺失:调块入Cache,但写主存的动作省不掉 → 多此一举
→ 写缺失:直接写主存,跳过Cache ✓(非写分配,更经济)

题图

本质上是两种一致的设计哲学:要么彻底用Cache缓冲写操作,要么彻底不缓冲写操作。混搭(如写直通+写分配)会让两种目标相互干扰,既增加了调块的开销,又没有减少主存写次数,得不偿失。

32b4cfb77dbc96572eb71688969426ae.svg

写操作完整流程 — 组合 一:写回法 + 写分配法(主流CPU,性能优先)

11e3c3e17a5fa8fc4796251aa6ed1b56.svg

写操作完整流程 — 组合二:写直通法 + 非写分配法(一致性优先)

e5aedc2f647e0495d10302002779c6d9.svg

Cache设计应考虑的问题

命中率是决定系统访存性能的核心指标,主要受3个关键因素影响:

  1. Cache容量:容量越大,命中率越高
  2. 关联度: 关联度与命中率强相关,关联度越高,冲突缺失越少,命中率越高。
  3. 主存块大小:块越大,空间局部性利用越好,但缺失损失也越大,需折中选择适中的块大小
  • 正向作用:主存块越大,能更好地利用空间局部性(程序访问某一地址后,大概率会访问相邻地址),一次性加载更多相邻数据到 Cache 中,减少后续的「冷启动缺失」,提升命中率。
  • 反向作用:主存块越大,缺失损失越大。一方面,Cache 缺失时,需要从主存读取的整块数据量更大,传输耗时更长;另一方面,块越大,Cache 中能存放的总块数越少,会加剧冲突缺失和容量缺失,反而可能导致命中率下降。

除此之外,Cache设计还需综合考虑多级结构、总线连接、DRAM配合等问题,这些都会直接影响系统整体性能。

单级/多级Cache、联合/分离Cache的选择

  • 现代CPU以多级片内Cache为主流,普遍集成L1、L2,甚至L3 Cache,均位于CPU芯片内
  • 常规分层设计:
  • L1 Cache:采用分离式Cache,指令Cache和数据Cache分开设置,避免指令与数据的访存冲突(避免流水线冲突中的结构冒险)
  • L2、L3 Cache:采用联合式Cache,指令和数据统一存放在同一个Cache中

Note

可以在L1-L2之间,L2-主存之间采用不同的写策略,例如写命中时, L1-L2采用写直通法,L2- 主存之间采用写回法。

两级Cache的访问逻辑

CPU访存优先级:先访问L1 Cache → L1缺失则访问L2 Cache → L2命中则缺失损失为L2的访问时间(远快于主存)→ L2也缺失则访问主存,同时将数据同步加载到L1和L2,此时缺失损失大幅提升。

1777400061055-1664c750-01b6-4ef8-bbb0-5a9abc032ca7.png

有关多级 cache 的设计,下列说法哪个是正确的?

A. L1 cache 更关注命中时间,L2 cache 更关注缺失率;

B. L1 cache 更关注缺失率,L2 cache 更关注命中时间;

答案

正确答案:A

  1. 一级Cache(L1):核心优化目标是命中时间(访问延迟)
    L1是CPU访存的第一站,与CPU核心直接相连,所有CPU访存请求都会优先访问L1,访问频率极高。它的设计优先级是最小化命中时的访问延迟,让CPU在命中时能以最快速度拿到数据,完全适配CPU的流水线主频。因此L1通常容量较小、采用指令/数据分离式设计、采用直接映射或低路数组相联(牺牲一定命中率),都是为了降低访问延迟,而非极致追求命中率。
  2. 二级Cache(L2):核心优化目标是缺失率
    L2是L1缺失后的后备缓存,只有L1未命中时,才会访问L2,访问频率远低于L1。它的设计优先级是最大化命中率,尽可能拦截L1的缺失请求,避免CPU访问速度极慢的主存。因此L2通常容量远大于L1,采用高路数组相联,会牺牲一定的访问延迟来提升命中率——因为一旦L2也缺失,CPU需要访问主存,带来的性能损失远大于L2稍高的访问延迟。

主存-总线-Cache 间的连接结构

当 CPU 访问 Cache 发生缺失(Miss) 时,主存需要将整个主存块传输给 Cache。此传输时间即为 缺失损失(Miss Penalty),其大小取决于两个关键因素:

  1. 总线传送方式:突发传送 vs 非突发传送
  2. 主存-总线-Cache 连接结构:窄形 / 宽形 / 交叉存储器

数据传输的三个阶段: 从主存读取 1 个字到 Cache,必须经过 3 个串行阶段

  1. 发送地址和读命令阶段:CPU 通过总线向主存发送目标地址和读操作命令,假设耗时1 个总线时钟周期。
  2. 主存准备数据阶段:主存根据地址,在存储阵列中找到并读取目标数据,假设耗时10 个总线时钟周期(主存核心延迟,耗时最长的阶段)。
  3. 总线传送数据阶段:主存将准备好的数据通过总线传送到 Cache,假设耗时1 个总线时钟周期。

因此,单字传输的单次完整耗时 = 1+10+1=12 个总线时钟周期,这是所有结构计算的基准。

两种总线传送方式

传送方式 描述 每次传输格式
非突发传送 每次传输一个数据前都需先发送地址 地址 → 数据 → 地址 → 数据 → …
突发传送 只发送首地址,后续数据地址自动递增 首地址 → 数据₁ → 数据₂ → 数据₃ → …

Note

判断规则:题目明确说明支持突发传送才使用突发方式;否则默认为非突发传送。


下面为三种核心连接结构的详细分析

假设:主存块大小 = 4个字,三个阶段时间分别耗时为 1、10、1 个时钟周期。

1777442374059-b3ac3b56-c9be-4449-987c-8e8ffb062e3b.png

窄形结构

定义:主存、总线和 Cache 之间每次按一个字的宽度进行传送。

结构特征:总线宽度 = 1 个字

非突发传送

每次传输流程:发地址(1) + 主存准备(10) + 传数据(1) = 12 周期/字

连续进行 4 次"送地址 → 读出 → 传送":\(\text{缺失损失} = 4 \times (1 + 10 + 1) = \mathbf{48} \text{ 个时钟周期}\)

1777384354708-fbc145e7-04c7-48cb-bcec-8f061e349ae1.png

突发传送

第 2、3、4 个字无需重新发送地址,节省 3 个周期:\(\text{缺失损失} = (1+10+1) + 3 \times (10+1) = 12 + 33 = \mathbf{45} \text{ 个时钟周期}\)

1777385341447-ff39c377-e83d-4092-b8f1-5bde4d24afc0.png

特点: 结构最简单,对总线位宽要求最低,兼容性最好;但传输效率最低,耗时最长,缺失损失最大,仅用于早期位宽极低的系统。


宽形结构

定义:主存、总线和 Cache 之间每次传送多个字。

结构特征:

  • 总线宽度 = 多个字(这是宽形结构的核心判断依据)
  • 主存通常采用单体多字存储器或多模块存储器同时启动实现
  • 若用多模块存储器:总线宽度 = 字宽 × 模块数,且只能同时启动同一行
  • 需要配套升级总线条数和主存接口部件,让主存每次能准备好对应位宽的多个字,减少传输次数。
非突发传送

以宽度 = 2字为例,连续进行 2 次"送地址 → 读出 → 传送"(每次传 2 字):\(\text{缺失损失} = 2 \times (1 + 10 + 1) = \mathbf{24} \text{ 个时钟周期}\)

1777385868030-ac43c9df-9d66-4838-bd65-b4e8d498c5dc.png

若宽度 = 4字(一次传完整块):\(\text{缺失损失} = 1 \times (1 + 10 + 1) = \mathbf{12} \text{ 个时钟周期}\)

1777396452479-63b259e2-2e98-44ae-ab1f-5db5b5e01343.png

突发传送(宽度 = 2字)

后 2 个字不需要重新发地址:\(\text{缺失损失} = (1+10+1) + (10+1) = 12 + 11 = \mathbf{23} \text{ 个时钟周期}\)

1777385967282-8596c002-acf9-47e9-ada0-97241235fdce.png

特点: 传输效率随总线位宽提升线性提升,缺失损失大幅降低;但对总线硬件、主存接口的成本和复杂度要求极高,位宽受主板布线、信号干扰、成本限制,无法无限增大。


多模块交叉存储器结构

定义:主存采用多模块交叉存取方式(轮流启动),总线和 Cache 每次按一个字宽传送。

结构特征:

  • 多模块存储器交叉编址(轮流启动,流水线式工作)
  • 总线宽度 = 单个模块的存储字长
  • 始终按突发传送方式计算

传输过程(4体交叉):发送首地址后,每隔 1 个时钟周期启动一个存储模块(流水线启动):

\(\text{缺失损失} = 1 \text{(发地址)} + 10 \text{(第1模块准备)} + 4 \times 1 \text{(依次传4字)} = \mathbf{15} \text{ 个时钟周期}\)

1777442213653-aa8bc2cc-de8d-4706-8e82-cf6a2c51c316.png

特点: 无需提升总线位宽,仅通过主存模块交叉流水,就将耗时从 48 周期降至 15 周期,传输效率远高于窄形结构,接近 4 字宽形结构的 12 周期,硬件成本和复杂度远低于宽形结构,性价比最优,是现代主存系统的主流设计

解题两步走

Note

第一步:判断总线传送方式\ 题目明确说"支持突发传送" → 突发传送\ 题目未说 / 说"不支持突发" → 非突发传送

第二步:判断连接结构\ 无多模块存储器 + 总线宽度 = 1字 → 窄形结构(非突发计算)\ 总线宽度 = 多个字 → 宽形结构\ 交叉编址多模块 + 总线宽度 = 单模块字长 → 交叉存储器结构(按突发计算)

习题演练

  1. 【2023-20】某存储器总线宽度为 64 位,总线时钟频率为 1 GHz,在总线上传输一个数据或地址需要 1 个时钟周期,不支持突发传送方式。若通过该总线连接 CPU 和主存,主存每次准备一个 64 位数据需要 6 ns,主存块大小为 32 B,则读取一个主存块需要的时间是( )

A. 8 ns  B. 11 ns  C. 26 ns  D. 32 ns

解析
  • 时钟周期 = 1/1GHz = 1 ns
  • 不支持突发 → 非突发传送
  • 无多模块条件,总线宽度 64 位 = 8 B = 1 字 → 窄形结构
  • 各阶段时间:公式,公式,公式
  • 主存块大小 32 B,总线宽度 8 B,字数 公式

公式

答案:D

  1. (袁书配套习题)假定 CPU 通过存储器总线读取数据的过程为:发送地址和读命令需 1 个时钟周期,存储器准备一个数据需 8 个时钟周期,总线上每传送 1 个数据需 1 个时钟周期。若主存和 cache 之间交换的主存块大小为 \(64\text{B}\),存取宽度和总线宽度都为 \(4\text{B}\),则 cache 的一次缺失损失至少是( )个时钟周期。
    A. 64
    B. 72
    C. 80
    D. 160
解析:
  • 无突发传送条件 → 非突发传送
  • 总线宽度 = 存取宽度 = 4 B = 1 字,无多模块 → 窄形结构
  • 字数 公式

公式

答案:D

  1. (袁书配套习题) 假定 CPU 通过存储器总线读取数据的过程为:发送地址和读命令需 1 个时钟周期,存储器准备一个数据需 8 个时钟周期,总线上每传送 1 个数据需 1 个时钟周期。若主存和 cache 之间交换的主存块大小为 \(64\text{B}\),存取宽度和总线宽度都为 \(8\text{B}\),则 cache 的一次缺失损失至少是多少个时钟周期?( )\ A. 64\ B. 72\ C. 80\ D. 160
解析:
  • 无突发传送条件 → 非突发传送
  • 总线宽度 = 8 B,无多模块 → 窄形结构(每次传 1 个 8 B 数据)
  • 字数 公式

公式

答案:C

  1. (袁书配套习题)假定采用多模块交叉存储器组织方式,存储器芯片和总线支持突发传送,CPU 通过存储器总线读取数据的过程为:发送首地址和读命令需 1 个时钟周期,存储器准备第一个数据需 8 个时钟周期(即 CAS 潜伏期=8),随后每个时钟周期总线上传送 1 个数据,可连续传送 8 个数据(即突发长度=8)。若主存和 cache 之间交换的主存块大小为 \(64\text{B}\),存取宽度和总线宽度都为 \(8\text{B}\),则 cache 的一次缺失损失至少为( )个时钟周期。
    A. 17
    B. 20
    C. 33
    D. 65
解析
  • 多模块交叉存储器 + 支持突发 → 交叉存储器结构(突发传送)
  • 字数 公式,突发长度恰好 = 8,一次突发传完整块

公式

答案:A

DRAM 结构、总线事务类型与 Cache 的配合设计

Cache 缺失时,必须从 DRAM 主存中读取整块数据,因此 DRAM 的内部结构、存储器总线的事务设计,必须与 Cache 的访问特性深度匹配,才能最大化传输效率,降低缺失损失。\ 指令执行过程中,一旦发生 Cache 缺失,就必须从 DRAM 中读取一整个主存块到 Cache 中,这个过程的耗时是 Cache 缺失损失的核心组成部分。因此,如何合理设计 DRAM 结构、如何让存储器总线在一次事务中高效传输一个主存块,必须与 Cache 设计统一考虑。

袁春风 教材典型设计案例:128 位存储器总线的内存条设计

1777442151147-867a3e9f-bf5a-4ac2-af40-253026a9c101.png

  1. 基础参数:

  2. 存储器总线位宽 = 128 位,即内存条一次最多能并行读出 128 位数据,与总线位宽完全匹配。

  3. 单个 DRAM 芯片规格:2Mb 容量,位宽 8 位,每个芯片内部有一个 512×8 的 SRAM 行缓冲 。
  4. 内存条配置:16 个上述 DRAM 芯片,组成一个 4MB 的内存条。

  5. 位宽匹配原理:16 个 DRAM 芯片并行工作,每个芯片每次输出 8 位数据,总位宽 = 16×8=128 位,完美匹配存储器总线的 128 位位宽,实现一次总线传输即可读写 128 位数据(低位交叉存储同时启动),适配 Cache 块的并行传输需求。

  6. 片内 SRAM 行缓冲的核心作用:DRAM 存储阵列按行组织,访问某一地址时,会把整行数据全部读取到片内的 SRAM 行缓冲中。\ 当 CPU 访问一块连续的主存区域(行地址相同的区域)时,无需再次访问 DRAM 存储阵列,直接从 SRAM 行缓冲中读取数据,SRAM 速度与 Cache 接近,远快于 DRAM 核心,大幅降低读取延迟。\ 16 个芯片的行缓冲总容量 = 16×(512×8 位)=8KB,足以缓存一整行主存数据,完全适配 Cache 块的连续读取需求。
  7. 总线事务优化:突发传输\ 传统总线事务:每传输 1 个字,都要执行一次「发送地址 - 发命令 - 传输 - 结束」的完整事务,开销极大,连续块传输效率极低。\ 突发传输优化:Cache 缺失读取主存块时,CPU 仅需发送一次首地址和读命令,主存就会自动连续读出该地址之后的多个连续数据(整个主存块),在一次总线事务中完成全部传输,无需重复发送地址和命令,大幅降低总线事务开销,完美适配 Cache 的块传输需求。
  8. DDR 系列 SDRAM 的进阶优化\ 现代 DDR SDRAM(DDR、DDR2、DDR3 等),针对 Cache 块传输做了两层核心优化:\ 芯片内部优化:采用多模块交叉存取结构 + 数据预取技术,让多个存储模块并行工作,提前预取连续的主存数据,减少数据准备延迟。\ 总线传输优化:采用双数据速率传输—— 在存储器总线时钟的上升沿和下降沿都传输一次数据,相比传统 SDRAM 仅在上升沿传输,传输速率直接翻倍,在相同总线频率下,主存到 Cache 的数据传送效率提升一倍,大幅降低 Cache 缺失的传输耗时