vault backup: 2026-06-15 22:55:55

This commit is contained in:
2026-06-15 22:55:55 +08:00
parent f8fb8553d3
commit 4fe0c53aaf
6 changed files with 857 additions and 36 deletions
@@ -57,6 +57,57 @@ graph TD
> [!note] 组相联 Cache 的命名
> "$n$ 路组相联"表示每组有 $n$ 个 Cache 行。2 路组相联 = 每组 2 行,4 路组相联 = 每组 4 行。
**地址解析格式**:
处理器发出的地址需要被拆分为 Tag / Index / Offset 三部分,不同映像方式的拆分规则不同:
```mermaid
graph LR
subgraph "直接映像地址"
A1["Tag"] --- A2["Index"] --- A3["Block Offset"]
end
subgraph "全相联地址"
B1["Tag"] --- B2["Block Offset"]
end
subgraph "组相联地址"
C1["Tag"] --- C2["Set Index"] --- C3["Block Offset"]
end
```
各字段含义:
| 字段 | 含义 | 位数计算 |
|------|------|----------|
| **Block Offset** | 块内偏移,定位块中具体哪个字节 | $\log_2(\text{块大小})$ |
| **Index**(直接映像) | 定位 Cache 中的哪一行 | $\log_2(\text{Cache 行数})$ |
| **Set Index**(组相联) | 定位 Cache 中的哪一组 | $\log_2(\text{组数})$ |
| **Tag** | 标记位,用于比对确认是否命中 | 地址总位数 - Index位数 - Offset位数 |
> [!example] 地址位分解示例
>
> **已知**:32 位地址,Cache 容量 16KB,块大小 64B,采用 4 路组相联。
>
> **计算各字段位数**:
>
> - Offset 位数 = $\log_2(64) = 6$ 位
> - Cache 行数 = $16\text{KB} / 64\text{B} = 256$ 行
> - 组数 = $256 / 4 = 64$ 组
> - Set Index 位数 = $\log_2(64) = 6$ 位
> - Tag 位数 = $32 - 6 - 6 = 20$ 位
>
> **地址 `0x1A2B3C47` 的解析**:
>
> | 字段 | 位数 | 二进制值 |
> |:----:|:----:|:---------|
> | Tag(高 20 位) | 31~12 | `0001 1010 0010 1011 0011` |
> | Set Index(6 位) | 11~6 | `110001` = 第 49 组 |
> | Offset(6 位) | 5~0 | `000111` = 块内第 7 字节 |
>
> > [!question] 如果改成直接映像呢?
> > 直接映像:Index = $\log_2(256) = 8$ 位,Tag = $32 - 8 - 6 = 18$ 位。
> > 同一地址会映射到第 `10010001` = 第 145 行,而不是第 49 组的某一行。
> > 相联度越高,Index 位数越少,Tag 位数越多,硬件比对逻辑越复杂。
#### 2.2 替换策略
| 策略 | 原理 | 特点 |
@@ -80,22 +131,78 @@ $$\text{不命中率} = w_I \times mr_I + w_D \times mr_D$$
其中 $w_I$、$w_D$ 分别为指令和数据的访问占比,$mr_I$、$mr_D$ 为各自的不命中率。
> [!example] 完整例题:地址序列命中/不命中判断
>
> **已知**:直接映像 Cache,4 行,块大小 16B(4 个字,每字 4B)。地址按字节编址。
>
> 地址结构:`[ Tag(高位) | Index(2位) | Offset(4位) ]`
>
> - Index 位数 = $\log_2(4) = 2$ 位(4 行直接映像)
> - Offset 位数 = $\log_2(16) = 4$ 位(块大小 16B)
>
> **访问以下地址序列**(十进制,假设地址按字节编址):
>
> | 序号 | 地址(十进制) | 地址(二进制) | Tag | Index | Offset | 块号 | 行号 | 结果 |
> |:----:|:-------------:|:--------------:|:---:|:-----:|:------:|:----:|:----:|:----:|
> | 1 | 0 | `00000000` | 0 | 00 | 0000 | 0 | 0 | **不命中**(冷启动) |
> | 2 | 4 | `00000100` | 0 | 00 | 0100 | 0 | 0 | **命中**(同一块) |
> | 3 | 16 | `00010000` | 0 | 01 | 0000 | 1 | 1 | **不命中**(新行) |
> | 4 | 132 | `10000100` | 8 | 01 | 0100 | 8 | 1 | **不命中**(替换行 1) |
> | 5 | 136 | `10001000` | 8 | 01 | 1000 | 8 | 1 | **命中**(同一块) |
> | 6 | 64 | `01000000` | 4 | 00 | 0000 | 4 | 0 | **不命中**(替换行 0) |
> | 7 | 48 | `00110000` | 3 | 00 | 0000 | 3 | 0 | **不命中**(替换行 0) |
> | 8 | 64 | `01000000` | 4 | 00 | 0000 | 4 | 0 | **不命中**(已被替换) |
>
> **Cache 行状态变化**:
>
> | 步骤 | 行 0 | 行 1 | 行 2 | 行 3 |
> |:----:|:----:|:----:|:----:|:----:|
> | 初始 | 空 | 空 | 空 | 空 |
> | 访问 0 | Tag=0 | 空 | 空 | 空 |
> | 访问 4 | Tag=0 | 空 | 空 | 空 |
> | 访问 16 | Tag=0 | Tag=0 | 空 | 空 |
> | 访问 132 | Tag=0 | **Tag=8** | 空 | 空 |
> | 访问 64 | **Tag=4** | Tag=8 | 空 | 空 |
> | 访问 48 | **Tag=3** | Tag=8 | 空 | 空 |
> | 访问 64 | **Tag=4** | Tag=8 | 空 | 空 |
>
> 命中率 = $2/8 = 25\%$
>
> > [!question] 观察到了什么?
> > 地址 64 在第 6 次访问时被调入,但第 7 次访问 48 时将它替换出,导致第 8 次再次访问 64 时又不命中——这就是典型的**颠簸(Thrashing)**现象。解决方法:提高相联度或增大 Cache 容量。
#### 3.2 平均访存时间
$$\text{平均访存时间} = \text{命中时间} + \text{不命中率} \times \text{不命中开销}$$
> [!example] 分离 Cache vs 混合 Cache
> [!example] 分离 Cache vs 混合 Cache(详解)
>
> **分离 Cache**(指令 16KB + 数据 16KB):
> - 指令不命中率 1%,数据不命中率 5%
> - 命中时间均为 1 周期,不命中开销 40 周期
> - 平均访存时间 $= 78\% \times (1 + 1\% \times 40) + 22\% \times (1 + 5\% \times 40) = 1.752$ 周期
> **已知条件**:
> - 程序中 78% 是指令访问(取指),22% 是数据访问(load/store)
> - 不命中开销均为 40 周期
>
> **混合 Cache**(32KB):
> - 不命中率 1.5%,但 load/store 命中时间 +1 周期
> - 平均访存时间 $= 78\% \times (1 + 1.5\% \times 40) + 22\% \times (2 + 1.5\% \times 40) = 1.782$ 周期
> **方案一:分离 Cache**(指令 16KB + 数据 16KB = 共 32KB)
> - 指令 Cache 不命中率 $mr_I = 1\%$,数据 Cache 不命中率 $mr_D = 5\%$
> - 命中时间均为 1 周期(指令和数据各有独立端口,不会冲突)
>
> **结论**:分离 Cache 更优(1.752 < 1.782)
> 逐步计算:
> 1. 指令部分平均访存时间 = $1 + 0.01 \times 40 = 1.40$ 周期
> 2. 数据部分平均访存时间 = $1 + 0.05 \times 40 = 3.00$ 周期
> 3. 加权平均 = $0.78 \times 1.40 + 0.22 \times 3.00 = 1.092 + 0.660 = \mathbf{1.752}$ 周期
>
> **方案二:混合 Cache**(统一 32KB)
> - 整体不命中率 $mr = 1.5\%$(因为容量相同,但指令和数据共享,冲突不命中增加)
> - 命中时间:取指仍为 1 周期,但 load/store 需 **+1 周期**(因为指令和数据共用端口,load/store 需要额外仲裁)
>
> 逐步计算:
> 1. 取指平均访存时间 = $1 + 0.015 \times 40 = 1.60$ 周期
> 2. 数据平均访存时间 = $2 + 0.015 \times 40 = 2.60$ 周期(命中时间 2 周期)
> 3. 加权平均 = $0.78 \times 1.60 + 0.22 \times 2.60 = 1.248 + 0.572 = \mathbf{1.820}$ 周期
>
> **结论**:分离 Cache 更优(1.752 < 1.820),节省约 $3.7\%$ 的平均访存时间。
>
> > [!question] 什么时候混合 Cache 反而更好?
> > 当数据和指令的访问模式不均匀时——比如某个阶段全是取指(循环),另一阶段全是数据访问——混合 Cache 能让 32KB 容量被充分利用,而分离 Cache 各 16KB 可能不够用。此时混合 Cache 的不命中率可能显著低于分离方案。
#### 3.3 CPU 时间与 Cache 的关系
@@ -103,6 +210,35 @@ $$CPU 时间 = IC \times (CPI_{exe} + \frac{\text{访存次数}}{指令} \times
### 四、17 种 Cache 优化技术
17 种优化技术围绕 Cache 性能公式中的三个因素展开,分类关系如下:
```mermaid
graph TD
ROOT["Cache 性能优化"] --> A["降低不命中率 (8种)"]
ROOT --> B["减少不命中开销 (5种)"]
ROOT --> C["减少命中时间 (4种)"]
A --> A1["增大块大小"]
A --> A2["增大 Cache 容量"]
A --> A3["提高相联度"]
A --> A4["伪相联 Cache"]
A --> A5["硬件预取"]
A --> A6["编译器控制预取"]
A --> A7["编译优化"]
A --> A8["Victim Cache"]
B --> B1["非阻塞 Cache"]
B --> B2["写合并"]
B --> B3["请求字优先"]
B --> B4["写缓冲"]
B --> B5["早重启"]
C --> C1["小容量简单 Cache"]
C --> C2["虚拟 Cache"]
C --> C3["流水化 Cache 访问"]
C --> C4["路预测"]
```
| 分类 | 技术 | 基本思想 | 影响 |
|:----:|------|----------|------|
| **降低不命中率**(8种) | 增大块大小 | 利用空间局部性 | 块过大会增加不命中开销 |
@@ -134,6 +270,58 @@ $$CPU 时间 = IC \times (CPI_{exe} + \frac{\text{访存次数}}{指令} \times
| **容量不命中**(Capacity) | Cache 容量不足 | 增大 Cache 容量 |
| **冲突不命中**(Conflict) | 映射冲突 | 提高相联度、Victim Cache |
### 六、综合计算题
> [!example] 综合计算题:Cache 参数计算与性能分析
>
> **已知条件**:
> - 处理器 32 位地址
> - Cache 容量 32KB
> - 块大小 64B
> - 采用 8 路组相联
> - LRU 替换策略
> - 写回策略
> - 命中时间 1 个时钟周期
> - 不命中开销 100 个时钟周期
> - 不命中率 2%
> - 程序中 30% 为访存指令(load/store)
>
> **问题 1:计算 Tag / Index / Offset 位数**
>
> - Cache 行数 = $32\text{KB} / 64\text{B} = 512$ 行
> - 组数 = $512 / 8 = 64$ 组
> - Offset 位数 = $\log_2(64) = 6$ 位
> - Index 位数 = $\log_2(64) = 6$ 位
> - Tag 位数 = $32 - 6 - 6 = 20$ 位
>
> | 字段 | Tag | Set Index | Block Offset |
> |:----:|:---:|:---------:|:------------:|
> | 位数 | 20 | 6 | 6 |
> | 位置 | [31:12] | [11:6] | [5:0] |
>
> **问题 2:计算平均访存时间**
>
> $$\text{AMAT} = \text{命中时间} + \text{不命中率} \times \text{不命中开销}$$
> $$= 1 + 0.02 \times 100 = 1 + 2 = \mathbf{3 \text{ 周期}}$$
>
> **问题 3:计算对 CPI 的影响**
>
> 假设理想 CPI(无 Cache 不命中)为 2.0:
>
> $$\text{实际 CPI} = \text{CPI}_{exe} + \text{访存指令比例} \times \text{不命中率} \times \text{不命中开销}$$
> $$= 2.0 + 0.30 \times 0.02 \times 100 = 2.0 + 0.6 = \mathbf{2.6}$$
>
> Cache 不命中使 CPI 增加了 $0.6$,性能下降了 $30\%$。
>
> **问题 4:如果将 Cache 容量增大到 64KB(不命中率降为 1%),是否值得?**
>
> 新的 CPI = $2.0 + 0.30 \times 0.01 \times 100 = 2.0 + 0.3 = 2.3$
>
> 性能提升 = $(2.6 - 2.3) / 2.6 = 11.5\%$
>
> > [!question] 如何权衡?
> > 64KB Cache 的面积是 32KB 的约 2 倍,但性能仅提升 11.5%。若芯片面积紧张,可考虑用其他优化技术(如提高相联度、硬件预取)来替代简单增大容量——这就是**17 种优化技术的组合运用**。
## 关联笔记
- [[计算机系统结构/复习文档/计算机系统结构基础与定量原理]]
- [[计算机系统结构/复习文档/总线与I/O系统]]