Cache主存地址映射
CPU访问内存时使用的是主存物理地址,但数据可能存放在Cache中。因此,需要一个机制能快速判断所需数据是否在Cache中(即“命中”,Hit),如果在,则需要将其从Cache中取出。这个将主存地址转换为Cache地址并进行查找的过程,就是地址映射。其核心任务是解决一个问题:一个主存块(Block)可以存放到Cache中的哪个或哪些位置。
1. 映射的基本原理与地址结构
为了实现快速查找,主存地址通常被逻辑上划分为几个部分。对于一个容量为 2M2^M2M 字节、按字节编址的主存和一个容量为 2C2^C2C 字节、块大小(或称行大小)为 2B2^B2B 字节的Cache,主存地址(M位)的通用结构如下:
| 高位部分 (标记) | 中间部分 (索引) | 低位部分 (块内偏移) |
|---|---|---|
| Tag | Index | Block Offset |
-
块内偏移 (Block Offset) : 用于确定所访问的数据在特定缓存块内的具体位置。其位数为。例如,如果块大小为64字节(B),则块内偏移需要6位 。
-
索引 (Index) : 用于确定主存块应该映射到Cache的哪一行(直接映射)或哪一组(组相联映射)。其位数取决于映射方式和Cache的组织结构 。
-
标记 (Tag) : 用于区分映射到同一个Cache行/组的不同主存块。当CPU提供的地址通过索引找到Cache中的某个位置后,需要将地址中的标记部分与该位置存储的标记进行比较,若一致且有效位为1,则判定为命中。
下面,详细讨论三种主要的映射方式。
2. 直接映射 (Direct Mapping)
直接映射是最简单的映射方式。它规定主存中的每一个块只能映射到Cache中的一个唯一固定的行 。这个映射关系通常由一个简单的模运算确定:
Cache行号 = 主存块号 mod Cache总行数
- 地址结构: 在直接映射中,主存地址被划分为三个部分: 标记(Tag) 、 行索引(Index) 和 块内偏移(Offset) 。
+------------------+----------------+----------------+
| Tag | Index | Block Offset |
+------------------+----------------+----------------+
- 工作原理:
- CPU发出一个主存地址。
- 根据地址中的Index部分,直接定位到Cache的唯一 一行。
- 比较该行存储的Tag与CPU地址中的Tag部分。
- 如果Tag匹配且该行有效,则Cache命中。根据Block Offset从该行中取出数据。
- 如果Tag不匹配或该行无效,则Cache未命中(Miss)。此时需要从主存中读取包含该地址的整个块,将其存入由Index指定的Cache行,并更新该行的Tag和有效位。
优点: 实现简单,硬件开销小,查找速度快,因为不需要复杂的比较电路,只需根据索引直接定位。
缺点: 冲突率高。即使Cache有大量空闲空间,如果两个频繁访问的主存块恰好需要映射到同一个Cache行,它们会不断地相互替换,导致“颠簸”(Thrashing)现象,严重降低Cache命中率 。
3. 全相联映射 (Fully Associative Mapping)
全相联映射提供了最大的灵活性。它允许主存中的任意一个块可以被放置到Cache中的任意一个行。
- 地址结构: 在这种方式下,由于没有固定的映射位置,所以不需要Index字段。主存地址被划分为两部分: 标记(Tag) 和 块内偏移(Offset) 。
+----------------------------------+----------------+
| Tag | Block Offset |
+----------------------------------+----------------+
- 工作原理:
- CPU发出一个主存地址。
- 将地址中的Tag部分与Cache中所有行的Tag同时进行比较。
- 如果任意一行的Tag匹配且有效,则Cache命中。根据Block Offset从该行中取出数据。
- 如果所有行都不匹配,则Cache未命中。此时需要启动替换策略选择一个牺牲行,从主存中读取新块替换之,并更新该行的Tag。
优点: 映射方式灵活,空间利用率最高,冲突率最低,因为只要Cache有空闲位置就能装入新块 。
缺点: 硬件实现极为复杂且昂贵。需要为Cache的每一行都配备一个比较器来并行比较Tag,这导致成本高、功耗大,并且随着Cache容量增大,比较逻辑的规模会急剧增长 。因此,纯粹的全相联映射通常只用于小容量的缓存,如TLB(Translation Lookaside Buffer)。
4. 组相联映射 (Set-Associative Mapping)
组相联映射是直接映射和全相联映射的一种折中方案,旨在平衡成本与性能 。它将Cache的行分成若干个组(Set),组与组之间采用直接映射,而每个组内部则采用全相联映射 。
Cache组号 = 主存块号 mod Cache总组数
如果一个组内包含k个行,就称为k路组相联(k-way set-associative)。
+------------------+----------------+----------------+
| Tag | Set Index | Block Offset |
+------------------+----------------+----------------+
- 工作原理:
- CPU发出一个主存地址。
- 根据地址中的Set Index部分,直接定位到Cache的唯一一个组。
- 将地址中的Tag部分与该组内所有k个行的Tag同时进行比较。
- 如果组内任意一行的Tag匹配且有效,则Cache命中。根据Block Offset从该行中取出数据。
- 如果组内所有行都不匹配,则Cache未命中。启动替换策略,从该组的k个行中选择一个牺牲行,从主存读取新块替换之,并更新该行的Tag。
优点: 通过在组内提供有限的灵活性,显著降低了冲突率(相比直接映射),同时硬件成本和复杂度远低于全相联映射(只需k个比较器)。
缺点: 相较于直接映射,实现略复杂,查找时间稍长;相较于全相联映射,仍然存在组间冲突的可能。
5. 综合计算示例与地址划分图
让我们通过一个具体的例子来固化理解。
假设条件:
- 主存地址空间:32位
- Cache容量:64 KB
- Cache块大小:64 B
- 映射方式:4路组相联 (4-way set-associative)
计算过程:
1. 计算块内偏移 (Offset) 位数:
- 块大小为 64 B = 262^626 B。
- 因此,Offset需要 6 位来寻址块内的每一个字节 。
- 地址范围:[0:5]
2. 计算组数和组索引 (Set Index) 位数:
- Cache总行数 = Cache容量 / 块大小 = 64 KB / 64 B = (64 * 1024) B / 64 B = 1024 行。
- Cache组数 = Cache总行数 / 路数 = 1024 / 4 = 256 组 。
- Set Index需要 log256log256log256 即8位来唯一标识每一个组 。(这里的log256log256log256是以2为底的,打不出来那个2,尴尬😢)
- 地址范围:[6:13]
3. 计算标记 (Tag) 位数:
- Tag位数 = 总地址位数 - Set Index位数 - Offset位数。
- Tag位数 = 32 - 8 - 6 = 18 位。
- 地址范围:[14:31]
地址划分图 (4路组相联):
31 14 13 6 5 0
+--------------------+----------------+----------------+
| Tag (18位) | Set Index (8位)| Offset (6位) |
+--------------------+----------------+----------------+
<-- 用于和组内各行Tag比较 --> <-- 定位到256个组之一 --> <-- 在64B块内寻址 -->
对比其他映射方式下的地址划分:
- 如果采用直接映射:
- 总行数 = 1024 行。Index需要log1024log1024log1024 即10位。(这里的log同样是以2为底的)
- Offset仍然是6位。
- Tag = 32 - 10 - 6 = 16位。
31 16 15 6 5 0
+--------------------+----------------+----------------+
| Tag (16位) | Index (10位) | Offset (6位) |
+--------------------+----------------+----------------+
-
如果采用全相联映射:
- 没有Index或Set Index。
- Offset仍然是6位。
- Tag = 32 - 6 = 26位。
31 6 5 0
+------------------------------------+----------------+
| Tag (26位) | Offset (6位) |
+------------------------------------+----------------+
1万+

被折叠的 条评论
为什么被折叠?



