Skip to content

Cache 和主存之间的映射方式

2026 大纲 三(六)2 Cache 和主存之间的映射方式

一个主存块该放进哪一行

主存的某一块要调进 Cache 时,必须先回答一个问题:它该放进哪一行? 这个答案必须是确定的,否则 CPU 拿到主存地址之后无从知道该去哪个位置寻找。三种映射方式就是对它的三种回答——只能放在唯一确定的那一行(直接映射)、可以放在任意一行(全相联)、只能放进唯一确定的那一组而组内任意(组相联)。差别本质上只是约束强弱:约束越强,查找越快、硬件越省,但冲突越多。

真正让这一节变简单的是下面这个视角:

🔴 标记就是块群号。 把主存按每 S 块切成一个个块群S 为 Cache 的行数或组数),规定块群内的第 i 块进第 i 行 / 组。这样行号由块在群里的位置本身确定,Cache 只需再记住"它来自哪个块群"——这就是标记存在的全部理由。tag=块号/S 这个式子由此不必背,它就是"第几个块群"。

地址随之被切成三段,n=t+c+b:块内地址 b=log2(块大小) 在最低位;中间是行号 / 组号(直接映射 c=log2(行数)、组相联 c=log2(组数)、全相联 c=0);最高位是标记 t=ncb。写成位区间就是块内地址 [b1:0]、行号 / 组号 [b+c1:b]、标记 [n1:b+c]

本节的算题几乎都是这三段的正算与反算,最容易漏的是中间那一步换算:题目给的通常是 Cache 数据区容量而不是行数,行数=数据容量÷块大小组数=行数÷k 是必经的中间步,跳过去就定不出组号位数。单位也要先统一——"块大小为 4 个字、每字 32 位"是 16 B。

交互可视化

加载可视化中...
加载可视化中...
加载可视化中...

一、直接映射

主存块号为 j 的块,只能放入 Cache 的第 jmod2c 行(2c 为 Cache 行数):

Cache 行号=主存块号mod2c图 7.27 cache 和主存之间的直接映射方式

图 7.27 cache 和主存之间的直接映射方式(袁春风《计算机组成与系统结构(第 3 版)》p238)

把主存按每 2c 块为一组切开,每组称一个块群

主存块号=块群号高位×2c+块群内序号低位

块群内序号正好就是 Cache 行号,而块群号正好就是标记。地址随之分成三段:

[ 标记t   |  Cache 行号c   |  块内地址b  ]

硬件为什么这么省事:行号就是地址中间那几位,直接取出来即可——除以 2c 取余等价于取二进制低 c 位,不需要真做除法。

代价是冲突最严重:相隔 2c 个块的主存块争同一行,即使 Cache 大部分是空的也会互相驱逐(抖动),因此命中率相对最低。

二、全相联映射

主存块可以放入 Cache 的任意一行,没有任何位置约束。

图 7.30 全相联映射方式下主存块和 cache 行对应关系

图 7.30 全相联映射方式下主存块和 cache 行对应关系(袁春风《计算机组成与系统结构(第 3 版)》p241)

没有位置约束,也就没有行号字段

[ 标记即完整的主存块号  |  块内地址b  ]

用块群的语言说:全相联相当于整个主存只有一个块群,块群号就是块号本身。它冲突率最低、命中率最高,但判命中要把标记与所有行同时比较,因此只用于行数很少的场合(如 TLB)。

三、组相联映射

Cache 分成 2q,每组 2s 行(称 2s 路组相联)。主存块号为 j 的块只能进第 jmod2q 组,组内任意一行

Cache 组号=主存块号mod2q图 7.32 组相联映射方式下主存块和 cache 行对应关系

图 7.32 组相联映射方式下主存块和 cache 行对应关系(袁春风《计算机组成与系统结构(第 3 版)》p243)

块群的说法照样成立,只是这次每 2q 块为一个块群,块群内第 i 块进第 i 组,标记仍等于块群号。

图 7.31 组相联映射方式的硬件实现

图 7.31 组相联映射方式的硬件实现(袁春风《计算机组成与系统结构(第 3 版)》p243)

图中是 2 路组相联,五个编号依次是:① 组号选中一组;② 标记与该组两行的标记分别送入两个比较器;③ 比较结果与该行有效位相与;④ 命中的那一路打开三态门放出数据;⑤ 两路命中信号相或得 hit,经多路选择器输出。

组相联并不是"第三种方式",而是含有两个端点的通用形式

路数 k组数退化为
k=1组数 = 行数直接映射(每组一行,进哪组就是进哪行)
1<k<N介于两者之间组相联
k=N(行数)组数 =1全相联(只有一组,组内任意行)

理解了这条谱,三种方式的所有对比都可以推出来,不必分别记忆。比较器就是最直接的例子:

🔴 比较器的个数 = 路数(直接映射 1 个、k 路组相联 k 个、全相联等于行数),每个比较器的位数 = 标记位数2s 路就是 2s 个比较器并联。所以路数不是越多越好——成本与比较延迟随路数线性上升,而命中率的提升到一定程度后就很小了,这正是实际 CPU 多取 2 / 4 / 8 路的原因。

判命中的条件在这里也要说全:有效位为 1 且标记相等,两者缺一不可。上电后 Cache 内容随机,某行的标记完全可能碰巧匹配。

四、Cache 的总容量

Cache 的每一行除数据外还要存判命中与管理所需的信息:

每行位数=1有效位+t标记+8×块大小数据+[1]脏位+[]替换算法位,总容量=行数×每行位数

脏位只在写回策略下存在(见 Cache 写策略);替换算法位只在需要替换决策时存在——直接映射没有选择余地,因而不需要。

由一个具体地址逐步算出组号、标记与总容量,附首址不对齐的块数(想核对自己切字段会不会切错时展开)

(一)字段划分与取值。 按字节编址,主存地址 32 位。Cache 采用 4 路组相联,数据区容量 64 KB,块大小 64 B,写回策略。

第一步,补出行数与组数——这是从容量到位数的必经中间步:

行数=64 KB64 B=1024,S=10244=256

第二步,定位数与位区间:

字段位数位区间
块内地址log264=6[5:0]
组号log2256=8[13:6]
标记3268=18[31:14]

第三步,算字段取值。0000 8B3CH =35644

块号=3564464=556,块内地址=35644556×64=60组号=556mod256=44,标记=556256=2

还原自检:(2×256+44)×64+60=35644

第四步,算总容量。写回策略,每行带脏位:

每行位数=1+18+64×8+1=532 总容量=1024×532=544768 =66.5 KB

数据区 64 KB,标记与控制位另占 2.5 KB——这就是总容量大于数据容量的来源。

(二)首址不对齐的块数。 数组 8192 B、块大小 64 B:

  • 首址块对齐:8192/64=128
  • 首址的块内地址为 32:(32+8192)/64=129

分子里那个首址的块内地址是关键——不对齐时首尾各占半块,块数要多 1。

(三)虚拟地址能不能直接切出组号——那条判据是怎么推出来的。 采用虚拟存储时 CPU 先拿到的是虚拟地址,而 Cache 用的是物理地址,于是有个问题:选组这一步是否必须等 TLB 翻译完?推理分两截。

第一截,组号可以来自虚地址。地址翻译只替换高位的页号,页内地址在虚拟地址和物理地址中完全相同。所以只要组号与块内地址这几位全部落在页内地址范围内——

log2(组数)+log2(块大小)log2(页大小)

——它们在虚地址和物理地址里就是同一串位,可以直接从虚拟地址切出来,与 TLB 查询并行进行。这正是现代 CPU 把地址翻译延迟隐藏掉的办法。

第二截,标记必须来自物理地址。标记落在页号那一段,而页号恰恰是翻译要替换的部分,不翻译拿不到。若拿虚地址的高位当标记,同一个物理块在不同进程的虚地址下会被认成不同的块(别名问题)。

两截合起来就是 VIPT(虚拟索引、物理标记):索引用虚地址所以能与 TLB 并行,标记用物理地址所以不出别名。若两截都用虚地址就是 VIVT,省掉了等待但要额外处理别名与进程切换时的冲刷;上面那条不等式不成立时,索引也只能等翻译完,退化成 PIPT。

考点速记

  1. 三种映射方式是一条连续谱k=1 退化为直接映射、k= 行数退化为全相联;比较器个数 = 路数,比较器位数 = 标记位数
  2. 标记 = 块群号;地址划分 n=t+c+b行数 = 数据容量 ÷ 块大小是必经的中间步;由地址反算时先剥块内地址得块号、再对组数取模,取模的对象是块不是字节
  3. 判命中的完整条件是有效位 =1 且标记相等;总容量 = 行数 ×(有效位 + 标记 + 数据 + 脏位 + 替换位),后两项是条件项——脏位只有写回策略才有,替换位只在需要替换决策时才有(直接映射没有)。

这一节在真题里被考过的形式(下方「真题练习」逐题对应):

  • 给 Cache 行数、路数、块大小和一个主存地址,问它映射到哪一组:三步走——地址 ÷ 块大小得块号,行数 ÷ 路数得组数,块号对组数取模得组号。⚠️ 别拿字节地址直接对组数取模。
  • 给地址位数、数据区容量、块大小、映射方式与写策略,问 Cache 行的位数或总容量至少是多少:先补出行数与组数定出标记位数,再逐项累加"有效位 + 标记 + 数据 +(脏位)+(替换位)"。写回有脏位、直写没有;直接映射没有替换位,组相联的 LRU 位数按路数定。
  • 问 Cache 中比较器的个数和位数:个数等于路数(全相联时等于行数),位数等于标记位数
  • 大题里划分各字段位数(虚页号 / 页内偏移 / TLB 标记 / Cache 组号 / 标记 / 块内地址):页大小定页内偏移,块大小定块内地址,组数定组号,剩下的归标记;虚拟地址位数与物理地址位数分别减去页内偏移即得虚页号与实页号位数。
  • 大题里问某数组占几个主存块或几个页(首址的块内地址+总字节数)/块大小首址不落在块边界上时首尾各占半块,要多算一块。
  • 大题里问"虚拟地址的哪几位可以用作 Cache 索引":判据是组号位数加块内地址位数不超过页内偏移位数。满足时这几位在虚实地址里完全相同,可直接取虚地址、与 TLB 查询并行;但标记必须来自物理地址
  • 大题里给一个主存块号,问它装入哪个组、对应的标记是什么:组号 = 块号对组数取模,标记 = 块号整除组数;反向自检 块号=标记×S+组号

易错:拿字节地址直接对组数取模。必须先除以块大小换算成块号。

易错:跳过"数据容量 ÷ 块大小 = 行数"这一步,直接拿容量去取对数定组号位数。

易错:算总容量时把脏位无条件加上。直写策略没有脏位,直接映射也没有替换位。

易错:数组占几块时直接用"总字节数 ÷ 块大小"。首址不对齐要先把首址的块内地址加进分子再向上取整。

易错:把"Cache 是 64 KB"理解成总的存储器件是 64 KB。这个数通常指数据区容量,标记与控制位另算。

教材出处
  • 袁春风《计算机组成与系统结构(第 3 版)》§7.5 高速缓冲存储器:直接映射与块群划分、图 7.27(p238~239)、全相联映射与图 7.30(p241)、组相联映射与图 7.31/7.32(p242~243)

相关知识

Cache 的基本原理Cache 中主存块的替换算法Cache 写策略虚拟存储器的硬件支持

真题练习