Appearance
层次化存储器的基本结构
2026 大纲 三(一)存储器的层次化结构。
快、大、便宜,只能挑两个
对存储器,人会同时提三个要求:速度要快、容量要大、每位成本要低。麻烦在于这三个要求互相冲突,而且冲突写在器件的物理原理里——SRAM 一个存储元要六个晶体管,所以快而贵、装不下多少;磁盘靠磁介质和机械臂,所以便宜、能装海量,但一次访问要毫秒级;DRAM 三项都居中。没有任何单一的存储技术能同时满足三条。
层次结构的解法不是找一种更好的器件,而是把它们叠起来用:把最活跃的一小部分数据放在最快的那层,其余的往下沉。这样 CPU"看到"的是一个速度接近最快层、容量接近最大层、每位成本接近最低层的存储系统。
但这个解法能成立有个前提,它不是硬件的性质:
🔴 程序的局部性原理——任一时刻程序实际活跃的地址范围(工作集)远小于它的整个地址空间。正因为如此,只把工作集放进小而快的那一层就够用了。局部性是程序的性质,硬件只是利用了它;一个真正随机访问整个地址空间的程序,任何层次结构都救不了。
理解这一章的钥匙就是这句话的两面:局部性说明了层次结构为什么有效,而每一层的具体设计(用什么映射、谁来管、怎么写回)则由未命中的代价决定——第三节会看到,两个层次所有的差别都能归到这一条上。
一、层次的整体形状
按器件从上到下排列,存储系统是一条阶梯:
三个指标沿这条阶梯同向变化:自上而下,速度递减、容量递增、每位价格递减。正因为三者的排序完全一致,任何一级都不能省——去掉上面的会变慢,去掉下面的会装不下。
但这条阶梯有四级,缓存关系却只有两组,因为寄存器不构成一个缓存层次:
🔴 判据是有没有"命中与否"的判断。Cache 与主存都要先查"我要的东西在不在本级",查不到才往下走;而寄存器由指令显式指名(
add R1, R2里的 R1、R2 写死在指令里)、由编译器静态分配,根本不存在查找、命中、替换这一套机制。
所以"存储系统分三层还是两层"这个问题要看从哪个角度说:按器件列举是多层(寄存器 / Cache / 主存 / 辅存),按缓存关系划分是两个层次(Cache–主存、主存–辅存)。题目问管理机制、映射方式、写策略时,说的一律是后者。顺带一提,主存在这里既是上层又是下层并不矛盾——它相对 Cache 是被缓存的一方,相对辅存是缓存方,这正是"层次"的含义。
二、局部性原理
时间局部性:刚被访问的单元,近期很可能再次被访问(循环体、计数变量)。空间局部性:刚被访问的单元,其相邻单元近期也很可能被访问(顺序执行的指令、数组元素)。
两者的判据不同,必须分开判断、分别给理由——这是判局部性那类题唯一的做法:
- 判时间局部性:数同一个单元被访问了几次,
次才有; - 判空间局部性:看地址是否顺序推进,连续访问有、大跨步或随机访问没有。
三类典型访问模式的判定结果:
| 访问模式 | 时间局部性 | 空间局部性 |
|---|---|---|
| 顺序遍历一遍(每个元素只碰一次) | 无 | 有 |
| 反复遍历同一小块数据 | 有 | 有 |
| 大跨步 / 随机访问 | 无 | 无 |
第一行最容易判错:顺序遍历一遍是"无时间局部性、有空间局部性"——每个元素读一次就再不碰了,所以没有时间局部性,但命中率仍然可以很高,因为一次调块把后面几个元素一起带进来了。反过来,嵌套循环里被内层反复扫过的数组既有时间也有空间局部性。
🔴 "局部性好"或"局部性差"是没有信息量的说法,必须指明是哪一种、并说清依据。典型反例:按列遍历一个行优先存储的二维数组,每次跨过一整行,调进来的整块里只用了一个元素——空间局部性被完全浪费,而时间局部性也没有。这也正是"调整循环顺序使其符合数组的存储顺序"能显著提速的原因。
三、两个层次
| 对比项 | Cache–主存 | 主存–辅存 |
|---|---|---|
| 解决的问题 | CPU 与主存的速度差距 | 主存的容量不足 |
| 上层 / 下层 | Cache(SRAM)/ 主存(DRAM) | 主存(DRAM)/ 辅存(磁盘、SSD) |
| 上下层速度比 | 约 | 约 |
| 传输单位 | 块(通常 64 B) | 页(通常 4 KB) |
| 映射方式 | 直接 / 组相联为主 | 全相联 |
| 管理者 | 硬件自动管理,对所有软件透明 | 操作系统管理,硬件提供支持(MMU、TLB) |
| 替换算法 | 硬件实现的 LRU 等 | 软件实现的近似 LRU |
| 写策略 | 写直达或写回 | 一律写回 |
| 未命中处理 | 硬件自动从主存调入 | 触发缺页异常,由 OS 从磁盘调入 |
| 未命中代价 | 几十~上百个时钟周期 | 约 |
| 对程序员 | 完全透明 | 呈现为虚拟地址空间 |
这张表不必硬记,因为加粗的三行全部由最后两行——未命中代价相差三个数量级——推出来:
- 代价高
值得用最灵活的全相联把缺失率压到最低,也值得花时间逐项查找; - 代价高
值得让软件介入做精细决策,多花几百个周期无所谓; - 代价高
写直达(每改一个字节就同步到磁盘)完全不可接受,只能写回。
反过来,Cache 缺失只有几十个周期,用软件处理的开销本身就超过了缺失代价,因此只能做进硬件;映射方式也必须选查找快的直接映射或组相联。两个层次遵循完全相同的原理,差别只在参数规模。
🔴 三处差别里最常被问反的是映射方式:主存–辅存层次通常采用全相联,不是直接映射。任何一页可以放进任何一个页框,正是为了把缺页率压到最低。
四、设计参数的权衡
没有哪个参数是"越大越好"。Cache 容量变大命中率提高,但成本上升、访问延迟也增大;相联度变高冲突缺失减少,但比较器增多、延迟增大;块大小变大能更充分利用空间局部性,但块数减少导致冲突增加,而且未命中代价随之增大——三个参数都存在最优值而不是单调更好。
页大小同理:页大则页表项少、TLB 覆盖范围大、磁盘传输效率高,代价是内部碎片大;页小则内部碎片小,代价是页表变大、TLB 缺失率升高。
为什么块是 64 B 而页是 4 KB:两者都是"一次传输多少"的选择,量级却差了 64 倍,原因在下层介质。主存的访问延迟主要是电路延迟,多传几十字节的边际成本很低,但也没必要太大;磁盘的访问延迟绝大部分花在寻道和旋转上(毫秒级),数据传输本身几乎不占时间——既然启动一次这么贵,就该一次多搬一些。所以页远大于块,本质上是被磁盘的机械特性决定的。
平均访问时间:为什么极低的缺页率仍会主导平均值
两级的平均访问时间逐层套:
⚠️ 这里的
用的是同时访问模型(访 Cache 的同时也启动主存)。若题面描述的是"先访 Cache、未命中再访主存",应换成 。按题面描述的流程选模型,别默认某一个。
把"缺页率虽低影响却很大"落到数字上(想弄清万分之二的缺页率凭什么能主导平均值时展开)
设
缺页率只有万分之二,却把平均访问时间从 4.4 ns 拉到约 1 μs——第二项
考点速记
- 三重矛盾(快、大、便宜)没有单一技术能同时满足,层次结构把器件叠起来用,成立的前提是程序的局部性原理——它是程序的性质不是硬件的性质。阶梯自上而下速度递减、容量递增、每位价格递减,三者同向变化。
- 寄存器不算一个缓存层次(由指令显式指名,没有"命中与否"的判断),所以缓存关系只有 Cache–主存与主存–辅存两组。两种局部性要独立判断、分别给理由。
- 两个层次在映射方式、管理者、写策略上的差别,全部源自未命中代价相差三个数量级;块大小与页大小都存在最优值,页远大于块是被磁盘的机械特性决定的。
这一节在真题里被考过的形式(下方「真题练习」逐题对应):
- 给一段嵌套循环的 C 代码,问它有没有时间/空间局部性:分开判。数组元素被外层循环反复扫到 ⇒ 有时间局部性;下标顺序推进 ⇒ 有空间局部性。⚠️ 只扫一遍的单层循环是"无时间、有空间",这一格最容易答反。
- 挑关于两个存储层次的错误叙述:四个选项分别踩交换单位(块 / 页)、替换算法实现者(硬件 / 软件)、写策略(都可回写)、映射方式。错点通常设在映射方式上——主存–外存层次通常是全相联,不是直接映射。
- 问 Cache 缺失处理与缺页处理哪个开销大、为什么(大题问答):缺页大得多,因为要访问磁盘(毫秒级、约
个时钟周期),而 Cache 缺失只访问主存(几十到上百个周期)。答题要给出这个数量级对比,不能只说"缺页大"。 - 问为什么 Cache 可以直写而修改页面总是回写(大题问答):同一条理由的应用——写直达要求每次写都同步到下一级,对主存尚可接受,对磁盘则是每改一个字节就启动一次毫秒级的机械操作,完全不可行。
易错:顺序遍历一遍判成"有时间局部性"。判据是同一单元被访问
次,读一遍不算。
易错:说"局部性好"而不指明是哪一种。按列遍历行优先数组是"两种都没有",按行遍历是"空间局部性好",题目要的是依据不是形容词。
易错:把主存–辅存层次的映射方式答成直接映射。它是全相联,理由是缺页代价太高、值得用最灵活的映射。
易错:平均访问时间的两种模型混用。题面说"同时访问"用
,说"先查 Cache 再访主存"用 。
教材出处
- 存储器的三重矛盾与层次化结构、三个指标沿层次同向变化:袁春风《计算机组成与系统结构》第 3 版 §5.1.2
- 程序访问的局部性原理、时间局部性与空间局部性的定义与举例:同上 §7.5.1
- Cache–主存与主存–辅存两个层次的对照、未命中代价与管理者的关系:同上 §7.5.1、§7.6
- 平均访问时间的逐级计算:同上 §7.5.4