diff --git a/计算机系统结构/复习文档/多处理器与并行处理.md b/计算机系统结构/复习文档/多处理器与并行处理.md index e5c0af9..272fa11 100644 --- a/计算机系统结构/复习文档/多处理器与并行处理.md +++ b/计算机系统结构/复习文档/多处理器与并行处理.md @@ -10,10 +10,10 @@ create time: 2026-06-15 10:00 ## 概述 -本文档讲解多处理器系统的基本概念、存储一致性模型、通信开销分析以及并行系统性能评估。多处理器和并行处理是现代计算机系统结构的重要方向,涉及软硬件协同设计的多个层面。 +本文档讲解多处理器系统的基本概念与分类、存储一致性模型(MSI 协议状态转移)、共享内存与分布式内存的架构对比,以及并行系统性能评估——包括远程访问 CPI 分析、计算/通信比、Amdahl 定律在并行系统中的应用。多处理器和并行处理是现代计算机系统结构的重要方向,涉及软硬件协同设计的多个层面。 > [!tip] 考试重点 -> 存储一致性的三个条件是论述题高频考点。远程访问对并行系统性能影响的计算(CPI 分析)在综合题中出现。 +> 存储一致性的三个条件是论述题高频考点。MSI 协议状态转移图常以简答题出现。远程访问 CPI 分析、Amdahl 定律的并行扩展在综合题中反复出现。 ## 正文 @@ -37,6 +37,36 @@ create time: 2026-06-15 10:00 | 适用场景 | 规则的数据并行(矩阵运算) | 不规则的并行任务 | | 灵活性 | 低(需同步) | 高(独立执行) | +#### 1.3 共享内存 vs 分布式内存 + +多处理器系统和多计算机系统的核心区别在于内存架构: + +| 对比维度 | 共享内存多处理器(SMP/NUMA) | 分布式内存多计算机(MPP/Cluster) | +|----------|---------------------------|----------------------------------| +| **内存视图** | 所有处理器看到统一的地址空间 | 每个节点有独立的本地内存 | +| **通信方式** | 通过 Load/Store 指令直接访问共享变量 | 通过消息传递(MPI)交换数据 | +| **编程模型** | 共享变量 + 锁/信号量 | 消息传递(Send/Receive) | +| **数据一致性** | 需要硬件一致性协议(如 MSI) | 由程序员显式管理 | +| **可扩展性** | 受限于内存互连带宽,通常 < 64 处理器 | 可扩展到数千节点 | +| **编程难度** | 较低(共享地址空间) | 较高(需显式管理数据分布) | +| **典型代表** | 大型服务器(SMP)、NUMA 服务器 | 超算集群(如 Summit) | + +```mermaid +flowchart LR + subgraph SMP["共享内存多处理器"] + P1["处理器 1"] --> SM["共享内存"] + P2["处理器 2"] --> SM + P3["处理器 3"] --> SM + end + subgraph MPP["分布式内存多计算机"] + N1["节点 1: CPU + 本地内存"] <-->|"消息传递"| N2["节点 2: CPU + 本地内存"] + N2 <-->|"消息传递"| N3["节点 3: CPU + 本地内存"] + end +``` + +> [!question] 思考 +> 为什么共享内存系统难以扩展到大规模?因为所有处理器竞争同一个内存系统的带宽。当处理器数量增加时,内存带宽成为瓶颈。NUMA(非统一内存访问)架构通过让每个处理器有本地内存来缓解,但远程访问延迟仍然远高于本地访问。 + ### 二、存储一致性(Cache Coherence) #### 2.1 问题背景 @@ -76,6 +106,28 @@ create time: 2026-06-15 10:00 | **S**(Shared) | 该行未被修改,多个 Cache 可能有副本 | | **I**(Invalid) | 该行无效 | +**MSI 协议状态转移图**: + +```mermaid +stateDiagram-v2 + [*] --> I + I --> M: "本地写 (Local Write)" + I --> S: "本地读命中 (Local Read Hit)" + S --> M: "本地写 (Local Write)" + M --> S: "远程读 (Remote Read, 总线读命中)" + M --> I: "远程写 (Remote Write, 总线写命中)" + S --> I: "远程写 (Remote Write, 总线写命中)" + I --> I: "远程写 (Remote Write, 监听到写)" +``` + +> [!note] 状态转移详解 +> - **本地操作**(本处理器发起):读命中时从 S 转移或保持 S;写操作将状态提升为 M(独占且已修改)。 +> - **远程操作**(其他处理器通过总线发起):当某处理器写数据时,其他持有该数据副本的 Cache 必须将其置为 I(无效);当某处理器读数据时,持有 M 状态的 Cache 需先将数据写回主存并将自身降为 S。 +> - **M -> S 的"写回"**:当远程处理器读取数据时,持有 M 状态的 Cache 必须先将修改的数据写回主存,然后转为 S。这保证了数据一致性。 + +> [!question] 思考 +> 为什么需要 M 状态?直接用 S 和 I 不行吗?如果只有 S 和 I,每次写操作都需要立即写回主存(write-through),这会导致大量总线流量。M 状态允许延迟写回(write-back),只有在其他处理器需要读该数据时才写回,大大减少了总线通信量。 + > [!question] 监听协议为什么不适合大规模系统? > 监听协议要求所有 Cache 监听总线,总线带宽成为瓶颈。当处理器数量增加时,总线冲突急剧增加,监听协议的可扩展性很差。目录协议通过点对点通信避免了这个问题。 @@ -89,7 +141,7 @@ create time: 2026-06-15 10:00 $$CPI = CPI_{base} + \text{远程访问率} \times \text{远程访问延迟(时钟数)}$$ -> [!example] 远程访问性能影响 +> [!example] 例题 1:远程访问性能影响 > 32 个处理器的集群,本地执行时间 10ns,CPI = 1.0。若有 0.5% 指令需远程访问(2000ns): > > 远程访问时钟 $= 2000 / 10 = 200$ @@ -98,6 +150,20 @@ $$CPI = CPI_{base} + \text{远程访问率} \times \text{远程访问延迟( > > **结论**:0.5% 的远程访问使 CPI 翻倍,性能下降 50%。 +> [!example] 例题 2:不同远程访问率的对比分析 +> 假设有 16 个处理器的 NUMA 系统,基本 CPI = 1.2,时钟周期 2ns,本地内存访问 40ns,远程内存访问 200ns。 +> +> | 场景 | 远程访问率 | 远程延迟(时钟) | 有效 CPI | 相对性能 | +> |------|:---------:|:-------------:|:--------:|:-------:| +> | 优化的并行程序 | 1% | 100 | $1.2 + 0.01 \times 100 = 2.2$ | 1.00 | +> | 一般并行程序 | 5% | 100 | $1.2 + 0.05 \times 100 = 6.2$ | 0.35 | +> | 通信密集程序 | 10% | 100 | $1.2 + 0.10 \times 100 = 11.2$ | 0.20 | +> +> **分析**:远程访问率从 1% 增加到 10%,CPI 从 2.2 飙升到 11.2,性能下降到原来的 1/5。这说明**数据局部性**对并行系统性能至关重要。优化并行程序的核心任务之一就是最小化远程访问比例。 + +> [!question] 思考 +> 如果远程内存访问延迟从 200ns 优化到 100ns(比如使用更快的互连网络),对通信密集程序(10% 远程访问率)的 CPI 影响有多大?新的 CPI = 1.2 + 0.10 * 50 = 6.2,相比原来的 11.2 几乎减半。硬件改进(降低远程延迟)和软件优化(减少远程访问率)同等重要。 + #### 3.2 并行计算的挑战 | 挑战 | 说明 | 应对方法 | @@ -106,13 +172,68 @@ $$CPI = CPI_{base} + \text{远程访问率} \times \text{远程访问延迟( | **通信开销** | 处理器间数据交换的延迟 | 优化通信模式、减少同步 | | **负载不均衡** | 各处理器工作量不等 | 动态调度、任务细分 | -#### 3.3 计算/通信比 +#### 3.3 Amdahl 定律在并行系统中的应用 + +Amdahl 定律指出,程序的加速比受限于串行部分的比例: + +$$S = \frac{1}{(1 - f) + \frac{f}{P}}$$ + +其中 $f$ 为可并行化的比例,$P$ 为处理器数量。 + +> [!example] 例题:Amdahl 定律计算 +> 某程序总执行时间中,串行部分占 10%($f = 0.9$)。 +> +> | 处理器数 $P$ | 加速比 $S$ | 效率 $S/P$ | 边际收益 | +> |:-----------:|:---------:|:---------:|:--------:| +> | 1 | 1.00 | 100% | - | +> | 2 | 1.82 | 91% | 0.82 | +> | 4 | 3.08 | 77% | 1.26 | +> | 8 | 4.71 | 59% | 1.63 | +> | 16 | 6.40 | 40% | 1.69 | +> | 32 | 7.80 | 24% | 1.40 | +> | 64 | 8.77 | 14% | 0.97 | +> | $\infty$ | 10.00 | 0% | - | +> +> **计算过程**(以 $P = 8$ 为例): +> +> $S = \frac{1}{0.1 + 0.9/8} = \frac{1}{0.1 + 0.1125} = \frac{1}{0.2125} \approx 4.71$ +> +> **关键发现**: +> +> 1. **理论上限**:即使无限处理器,加速比也不会超过 $1/0.1 = 10$ 倍。这就是串行瓶颈。 +> 2. **边际收益递减**:从 16 个到 32 个处理器,只增加了 1.4 倍加速比(边际收益列)。处理器翻倍,收益远小于翻倍。 +> 3. **效率下降**:处理器越多,每个处理器的利用率越低。 + +> [!important] Amdahl 定律的实践启示 +> - 与其追求更多处理器,不如先优化串行部分。将串行比例从 10% 降到 5%,理论上限从 10 倍提升到 20 倍。 +> - 在实际系统中,通信开销进一步拉低并行效率。真正有效的加速比远低于 Amdahl 定律的预测。 +> - 这就是为什么"弱扩展"(增大问题规模以保持效率)比"强扩展"(固定问题规模增加处理器)在实践中更有意义。 + +#### 3.4 计算/通信比 $$\text{计算/通信比} = \frac{\text{计算量}}{\text{通信量}}$$ - 随数据规模增加而**增大**(计算量增长快于通信量) - 随处理器数目增加而**减小**(每个处理器分到的计算减少,但通信开销相对增加) +> [!example] 例题:计算/通信比的趋势分析 +> 一个矩阵乘法问题,矩阵规模为 $N \times N$,使用 $P$ 个处理器并行计算。 +> +> - 计算量:$O(N^3 / P)$(每个处理器分担的计算) +> - 通信量:$O(N^2 / \sqrt{P})$(2D 分块方案下的通信开销) +> - 计算/通信比:$O(N^3 / P) \div O(N^2 / \sqrt{P}) = O(N / \sqrt{P})$ +> +> **趋势演示**(设 $N = 1000$, $P = 16$): +> +> | 参数变化 | 计算量(相对) | 通信量(相对) | 计算/通信比 | +> |----------|:-----------:|:-----------:|:----------:| +> | $N=1000, P=16$ | 62.5 | 250 | 0.25 | +> | $N=2000, P=16$ | 500 | 500 | 1.00 | +> | $N=4000, P=16$ | 4000 | 1000 | 4.00 | +> | $N=2000, P=64$ | 125 | 250 | 0.50 | +> +> **结论**:规模翻倍(1000→2000→4000),计算/通信比快速提升(0.25→1→4),而增加处理器(16→64)则计算/通信比下降(1→0.5)。这就是为什么"规模化并行"(scaling up the problem)比"增加处理器"更能提高并行效率。 + > [!tip] 提高并行效率的关键 > 尽量提高计算/通信比:增大问题规模、优化通信模式(减少全局通信)、使用高带宽低延迟的互连网络。 diff --git a/计算机系统结构/复习文档/存储系统与Cache.md b/计算机系统结构/复习文档/存储系统与Cache.md index 975cf84..a1f50b2 100644 --- a/计算机系统结构/复习文档/存储系统与Cache.md +++ b/计算机系统结构/复习文档/存储系统与Cache.md @@ -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系统]] diff --git a/计算机系统结构/复习文档/总线与I_O系统.md b/计算机系统结构/复习文档/总线与I_O系统.md index ea0635d..89f206f 100644 --- a/计算机系统结构/复习文档/总线与I_O系统.md +++ b/计算机系统结构/复习文档/总线与I_O系统.md @@ -11,10 +11,10 @@ create time: 2026-06-15 10:00 ## 概述 -本文档讲解总线的基本概念、分离事务总线的工作原理,以及 I/O 系统的性能评价指标。总线是连接处理器、存储器和 I/O 设备的通信通道,其设计直接影响系统整体性能。 +本文档讲解总线的基本概念、分类与仲裁方式、同步/异步总线的工作机制、分离事务总线的工作原理,以及 I/O 系统的核心组成——DMA 与中断机制。最后讨论 I/O 系统的性能评价与计算方法。总线是连接处理器、存储器和 I/O 设备的通信通道,其设计直接影响系统整体性能。 > [!tip] 考试重点 -> 分离事务总线的概念和别称常以选择题出现。I/O 系统性能评价指标需理解各维度的含义。 +> 分离事务总线的概念和别称常以选择题出现。DMA 三种工作模式的对比、中断处理流程是简答题高频考点。I/O 系统性能计算(总线带宽、CPU 占用率)是综合题常客。 ## 正文 @@ -36,7 +36,65 @@ create time: 2026-06-15 10:00 - **总线宽度**:数据线的位数(如 64 位) - **总线频率**:总线时钟频率(如 800 MHz) -### 二、分离事务总线 +三者之间的关系: + +$$\text{总线带宽} = \text{总线宽度} \times \text{总线频率} \times \text{每个时钟传输次数}$$ + +> [!question] 思考 +> 一条 64 位宽、800 MHz 的总线,理论峰值带宽是多少?如果每个时钟周期传输 1 次,答案是 $64 \div 8 \times 800 = 6400\ \text{MB/s}$。若采用 DDR(双倍数据率)技术,带宽翻倍为 12800 MB/s。这就是为什么现代总线普遍采用 DDR 技术。 + +#### 1.3 总线仲裁 + +多个设备可能同时请求使用总线,**总线仲裁器**(Arbiter)决定谁获得使用权。仲裁方式分为两大类: + +| 仲裁方式 | 仲裁器位置 | 优点 | 缺点 | 典型策略 | +|----------|:----------:|------|------|----------| +| **集中式仲裁** | 专用硬件仲裁器 | 决策快、确定性强 | 仲裁器是单点瓶颈 | 菊花链、轮询、独立请求 | +| **分布式仲裁** | 无集中仲裁器,各设备自行协商 | 可扩展性好 | 延迟不确定 | 自举分布式、冲突检测 | + +**集中式仲裁的三种策略**: + +- **菊花链仲裁**:Grant 信号沿设备链逐级传递,离仲裁器越近优先级越高。简单但不公平,低优先级设备可能饿死。 +- **轮询仲裁**:仲裁器按固定顺序轮询各设备。公平但效率不高。 +- **独立请求仲裁**:每个设备有独立的请求/应答线。速度快、灵活,但线数随设备数增加。 + +> [!question] 思考 +> 如果系统中有 16 个 I/O 设备,独立请求仲裁需要多少对请求/应答线?答案是 16 对。设备再多,线数就成了问题——这就是为什么大规模系统倾向于使用分布式仲裁或层级式仲裁。 + +### 二、同步总线与异步总线 + +总线通信的同步方式决定了设备间如何协调时序。 + +#### 2.1 同步总线 + +所有操作由**统一的时钟信号**驱动。发送方和接收方在时钟边沿进行数据采样。 + +- 优点:控制简单、速度快 +- 缺点:所有设备必须以同一时钟频率工作,灵活性差;总线长度受时钟偏移(clock skew)限制 + +#### 2.2 异步总线 + +没有统一时钟,通过**握手信号**(Handshake)协调通信。典型的四周期握手协议如下: + +```mermaid +sequenceDiagram + participant S as "发送方" + participant R as "接收方" + + S->>R: "1. 数据就绪 (Data Valid)" + R->>S: "2. 数据接收 (Data Ack)" + S->>R: "3. 撤销数据就绪" + R->>S: "4. 撤销数据接收" + Note over S, R: "一个完整的四周期握手完成" +``` + +- 优点:允许不同速度的设备通信,灵活性高 +- 缺点:握手开销增加了延迟,速度比同步总线慢 + +> [!note] 半同步总线 +> 实际系统常采用**半同步总线**:以时钟为基本定时参考,但引入 `Wait` 信号允许慢速设备插入等待周期。兼顾了同步的效率和异步的灵活性。 + +### 三、分离事务总线 **核心思想**:将一个总线事务分成**请求**和**响应**两个阶段,在请求和响应之间的空闲时间内,总线可以供给其他 I/O 设备使用。 @@ -48,7 +106,7 @@ sequenceDiagram C->>B: 请求阶段(发送地址+命令) B->>M: 内存准备数据 - Note over B: 总线空闲,可服务其他请求 + Note over B: "总线空闲, 可服务其他请求" M->>B: 响应阶段(返回数据) B->>C: 数据到达 ``` @@ -65,9 +123,9 @@ sequenceDiagram > [!note] 分离事务总线的优势 > 传统总线在一个事务完成前一直被占用,分离事务总线在等待内存响应时释放总线,允许多个设备并发使用,显著提高总线利用率。 -### 三、I/O 系统性能 +### 四、I/O 系统性能 -#### 3.1 性能评价维度 +#### 4.1 性能评价维度 | 维度 | 含义 | 衡量指标 | |------|------|----------| @@ -79,7 +137,7 @@ sequenceDiagram > [!question] I/O 系统性能为什么重要? > 随着处理器速度远超 I/O 设备速度,I/O 瓶颈日益突出。一个 I/O 操作可能耗时数毫秒,而 CPU 一个时钟周期仅数纳秒,巨大的速度差使得 I/O 系统可能成为整个系统的瓶颈。 -#### 3.2 I/O 系统的瓶颈问题 +#### 4.2 I/O 系统的瓶颈问题 ``` CPU 速度: ~GHz (纳秒级) @@ -93,7 +151,45 @@ CPU 速度: ~GHz (纳秒级) - I/O 和 CPU 的性能不匹配时,I/O 系统成为瓶颈 - 需要通过缓存、缓冲、异步 I/O 等技术缓解 -### 四、DMA 与中断 +#### 4.3 I/O 性能计算 + +> [!example] 例题:总线带宽与 I/O 吞吐量 +> 某计算机系统参数如下: +> +> - 总线宽度:64 位 +> - 总线频率:200 MHz +> - 每个总线周期传输 1 次数据 +> - 系统通过 DMA 将磁盘数据传入内存 +> - DMA 采用块传输模式,每个总线事务的开销为 2 个周期(地址+命令 1 周期,数据传输 1 周期) +> +> **问题 1**:总线的理论峰值带宽是多少? +> +> **解**: +> +> $\text{理论带宽} = 64\text{bit} \times 200\text{MHz} = 1600\text{MB/s}$ +> +> **问题 2**:考虑事务开销后,实际有效带宽是多少? +> +> **解**: +> +> 每传输 64 bit(8 B)数据需要 2 个周期(1 个开销 + 1 个数据) +> +> $\text{有效带宽} = \frac{8\text{B}}{2 \times 5\text{ns}} = 800\text{MB/s}$ +> +> **问题 3**:如果有 4 个磁盘控制器同时以 DMA 方式向内存写入数据,每个控制器持续传输速率为 150 MB/s,总线能否支持? +> +> **解**: +> +> 总需求带宽:$4 \times 150 = 600\text{MB/s}$ +> +> 有效带宽 800 MB/s > 600 MB/s,可以支持。但总线利用率已达 $600/800 = 75\%$,余量不多。如果再增加设备或提高单设备速率,总线将成为瓶颈。 + +> [!question] 思考 +> 为什么分离事务总线能提高 I/O 系统的有效带宽?因为它在等待内存响应期间释放总线给其他设备使用,减少了总线空闲时间。在上例中,如果采用传统总线,每个 DMA 事务期间总线被独占(即使设备在准备数据),有效带宽会进一步下降。 + +### 五、DMA 与中断 + +#### 5.1 三种 I/O 控制方式对比 | 方式 | 原理 | CPU 参与度 | 适用场景 | |------|------|:----------:|----------| @@ -101,12 +197,72 @@ CPU 速度: ~GHz (纳秒级) | 中断 | I/O 完成后通知 CPU | 中 | 低速设备 | | **DMA** | 直接内存访问,不经过 CPU | 低 | 高速设备 | -DMA 的工作流程: +#### 5.2 DMA 工作流程 1. CPU 设置 DMA 传输参数(源地址、目的地址、传输长度) 2. DMA 控制器接管总线,直接在设备和内存间传输数据 3. 传输完成后,DMA 控制器向 CPU 发中断通知 +#### 5.3 DMA 的三种工作模式 + +DMA 控制器在传输过程中根据总线使用权的不同,分为三种工作模式: + +| 模式 | 总线使用方式 | CPU 影响 | 传输效率 | 适用场景 | +|------|------------|:--------:|:--------:|----------| +| **单字传输**(Cycle Stealing) | 每传一个字就释放总线给 CPU | 每个字都要"偷"一个总线周期,CPU 频繁被打断 | 低 | CPU 对延迟敏感的场景 | +| **块传输**(Block Transfer) | 一次占用总线传完整个数据块 | CPU 长时间无法使用总线 | 高 | 大块数据连续传输 | +| **请求传输**(Demand Transfer) | DMA 检查 DREQ 信号,设备未就绪则释放总线 | 仅在设备有数据时才占用总线 | 中等 | 设备速度不确定的场景 | + +> [!question] 思考 +> 假设 CPU 每秒执行 1 亿条指令,DMA 以单字模式从磁盘读取数据,总线宽度 32 位,总线周期 100ns。每"偷"一个周期 CPU 损失一条指令的时间。若传输 1 MB 数据,CPU 会损失多少执行时间?答案:$1\text{MB} \div 4\text{B} = 250000$ 个周期,损失 $250000 \times 100\text{ns} = 25\text{ms}$,占 1 秒的 2.5%。这就是为什么高速传输通常选择块传输模式。 + +#### 5.4 中断处理的完整流程 + +当 I/O 设备完成操作后,通过中断通知 CPU。CPU 响应中断的完整过程如下: + +```mermaid +flowchart TD + A["中断请求"] --> B{"CPU 响应?"} + B -- "否" --> A + B -- "是" --> C["关中断"] + C --> D["保护现场: 保存 PC, PSW 等"] + D --> E["识别中断源: 确定哪个设备中断"] + E --> F["跳转中断服务程序"] + F --> G["执行中断服务: 处理 I/O 数据"] + G --> H["恢复现场: 恢复 PC, PSW 等"] + H --> I["开中断"] + I --> J["返回断点继续执行"] +``` + +> [!important] 关键步骤说明 +> - **保护现场**:必须保存程序计数器(PC)和程序状态字(PSW),确保中断返回后能正确继续执行。 +> - **中断源识别**:多个设备同时中断时,需要确定优先级。硬件向量法比软件轮询法快得多。 +> - **嵌套中断**:中断服务期间是否允许新的更高优先级中断嵌入?这取决于是否在服务程序中开中断。 + +#### 5.5 中断 vs DMA 的 CPU 占用率计算 + +> [!example] 例题:I/O 系统 CPU 占用率 +> 一个系统中,CPU 时钟频率 500 MHz,磁盘控制器以 DMA 方式传输数据。 +> +> - DMA 采用块传输模式,每次传输 4 KB 数据块 +> - 传输前 CPU 需要 1000 个时钟周期设置 DMA 控制器 +> - 传输完成后 DMA 中断 CPU,中断处理需要 500 个时钟周期 +> - 磁盘持续数据传输速率为 40 MB/s +> +> **问题**:CPU 用于该磁盘 I/O 的时间比例是多少? +> +> **解**: +> +> 每个 4 KB 块需要 CPU 参与的周期数:$1000 + 500 = 1500$ 个周期 +> +> 每秒传输的块数:$40\text{MB/s} \div 4\text{KB} = 10000$ 块/秒 +> +> 每秒 CPU 花在 I/O 上的周期数:$10000 \times 1500 = 1.5 \times 10^7$ 周期/秒 +> +> CPU 占用率:$1.5 \times 10^7 \div 5 \times 10^8 = 3\%$ +> +> **结论**:DMA 的 CPU 占用率仅 3%。如果采用中断方式逐字传输,每次传输 1 个字(4B)都需要一次中断,CPU 占用率将大幅飙升。 + ## 关联笔记 - [[计算机系统结构/复习文档/计算机系统结构基础与定量原理]] - [[计算机系统结构/复习文档/存储系统与Cache]] diff --git a/计算机系统结构/复习文档/指令系统设计.md b/计算机系统结构/复习文档/指令系统设计.md index 9ed144d..6b1d012 100644 --- a/计算机系统结构/复习文档/指令系统设计.md +++ b/计算机系统结构/复习文档/指令系统设计.md @@ -50,6 +50,18 @@ create time: 2026-06-15 10:00 2. 使用频率低的指令分配长操作码 3. 短操作码不能是长操作码的前缀 +```mermaid +graph TD + A["Determine Instruction Word Length"] --> B["Allocate Address Fields"] + B --> C["Calculate Remaining Bits for Opcode"] + C --> D["Assign Short Opcodes to Frequent Instructions"] + D --> E["Use Remaining Encoding Space as Extension Flag"] + E --> F["Allocate Longer Opcodes for Less Frequent Instructions"] + F --> G{"All Instruction Types Covered?"} + G -- No --> E + G -- Yes --> H["Design Complete"] +``` + > [!example] 例:12 位指令字长,3 位地址字段 > > | 类型 | 操作码 | 地址数 | 可用编码空间 | @@ -58,6 +70,51 @@ create time: 2026-06-15 10:00 > | 二地址 | 6 位 | 2 | 余下 4 个 × $2^3 = 32$(用 8 个) | > | 单地址 | 9 位 | 1 | 余下编码 × $2^3 = 190$ 条 | +> [!example] 综合例题:扩展操作码指令格式设计 +> +> **题目**:某计算机指令字长 16 位,每个地址字段 4 位。要求设计扩展操作码,使得: +> - 三地址指令 15 条 +> - 二地址指令 15 条 +> - 单地址指令 15 条 +> - 零地址指令尽可能多 +> +> 求各类指令的编码方案及零地址指令的最大数量。 +> +> **解题步骤**: +> +> **第一步**:确定各字段位数 +> +> 指令字长 16 位,地址字段 4 位。三地址指令需 $4 \times 3 = 12$ 位用于地址,剩余 $16 - 12 = 4$ 位用于操作码。 +> +> **第二步**:计算三地址指令空间 +> +> 4 位操作码最多表示 $2^4 = 16$ 种编码,实际需要 15 条,剩余 1 个编码作为扩展标志。 +> +> **第三步**:计算二地址指令空间 +> +> 二地址指令:操作码 = 4 位(前缀)+ 4 位(第一地址字段扩展)= 8 位,地址占 $4 \times 2 = 8$ 位。由扩展标志 $1 \times 2^4 = 16$ 种编码,实际用 15 条,剩余 1 个继续扩展。 +> +> **第四步**:计算单地址指令空间 +> +> 单地址指令:操作码 = 8 位 + 4 位 = 12 位,地址占 4 位。由扩展标志 $1 \times 2^4 = 16$ 种编码,实际用 15 条,剩余 1 个继续扩展。 +> +> **第五步**:计算零地址指令空间 +> +> 零地址指令:操作码占满 16 位。由扩展标志 $1 \times 2^4 = 16$ 条。 +> +> **结果汇总**: +> +> | 类型 | 操作码位数 | 地址字段位数 | 指令数量 | +> |:----:|:---------:|:----------:|:-------:| +> | 三地址 | 4 位 | 12 位 | 15 条 | +> | 二地址 | 8 位 | 8 位 | 15 条 | +> | 单地址 | 12 位 | 4 位 | 15 条 | +> | 零地址 | 16 位 | 0 位 | 16 条 | +> +> 共计 $15 + 15 + 15 + 16 = 61$ 条指令。 +> +> **关键技巧**:每层保留的扩展标志数量决定了下一层的编码空间。如果某层需要 $k$ 条指令,操作码有 $n$ 位,则保留 $2^n - k$ 个标志位用于向下扩展。 + ### 三、RISC vs CISC | 特性 | RISC | CISC | @@ -71,6 +128,22 @@ create time: 2026-06-15 10:00 | 寄存器 | 多(32+ 通用寄存器) | 少 | | 编译器 | 依赖编译器优化 | 硬件承担更多 | +**历史背景与设计理念**: + +20 世纪 70 年代末,IBM 的 John Cocke 等人通过研究发现,实际程序中 80% 的执行时间只用到了 20% 的指令——这就是著名的 **80/20 法则**。基于此观察,精简指令集(RISC)的设计理念应运而生: + +- **CISC 路线**(以 Intel x86 为代表):通过增加指令数量和复杂度来缩小"语义鸿沟",一条指令完成更多工作,减少程序体积和访存次数。代价是硬件复杂度高、设计周期长、功耗大。 +- **RISC 路线**(以 ARM、MIPS 为代表):只保留最常用的简单指令,通过编译器组合来实现复杂功能。硬件简单、时钟频率高、易于流水线化。 + +> [!note] 历史上的关键节点 +> - 1964 年:IBM System/360 奠定 CISC 基础,引入微程序控制 +> - 1980 年:Berkeley RISC-I 和 Stanford MIPS 项目验证了 RISC 可行性 +> - 1985 年:ARM1 诞生,RISC 进入商业领域 +> - 如今:x86 处理器内部将 CISC 指令翻译为类 RISC 微操作执行,两种路线殊途同归 + +> [!question] RISC 和 CISC 哪个更好? +> 这是一个没有标准答案的问题。CISC 减少了指令条数但增加了硬件复杂度;RISC 简化了硬件但增加了程序体积。现代处理器实际上融合了两者优点——x86 内核已经是"RISC 核心 + CISC 前端"的混合架构。 + > [!warning] RISC 的常见误解 > - RISC 不是"指令功能简单",而是"指令数量少、格式规整" > - RISC 的单周期是指理想情况,Cache 不命中等仍会导致多周期 @@ -90,6 +163,58 @@ create time: 2026-06-15 10:00 | 基址寻址 | EA = R + A | 数组基址 | | 变址寻址 | EA = A + R | 数组下标 | +```mermaid +graph TD + A["Instruction Decoded"] --> B{"Addressing Mode"} + B -- Immediate --> C["Operand = Address Field A"] + B -- Register --> D["EA = Register R"] + B -- Direct --> E["EA = Address Field A"] + B -- Indirect --> F["Fetch Content at A"] + F --> G["EA = Content"] + B -- Base --> H["EA = Base Register R + Offset A"] + B -- Index --> I["EA = Index Register R + Base Address A"] +``` + +> [!question] 基址寻址和变址寻址看起来公式一样(都是 R + A),有什么区别? +> 区别在于**使用场景和硬件支持**。基址寻址中,R 由操作系统设置(程序不能改),A 是指令中的偏移量,用于实现**程序重定位**和**数组基址**访问。变址寻址中,R 由用户程序修改(如循环中 R++),A 是固定基地址,用于实现**数组下标遍历**。在 RISC 架构中,两者通常不做区分,统一用寄存器+偏移量的寻址方式。 + +> [!example] 综合题:指令格式设计 +> +> **题目**:某计算机字长 32 位,指令字长 16 位。要求: +> - 支持 4 种操作:加、减、乘、除(操作码 2 位即可) +> - 需要 64 个通用寄存器 +> - 支持立即数寻址和寄存器寻址两种模式 +> - 立即数范围:$-128 \sim 127$ +> +> 设计最优的指令编码格式。 +> +> **解题步骤**: +> +> **第一步**:确定各字段所需位数 +> +> | 字段 | 位数 | 理由 | +> |:----:|:----:|------| +> | 操作码 | 2 位 | 4 种操作,$2^2 = 4$ | +> | 寄存器编号 | 6 位 | 64 个寄存器,$\log_2 64 = 6$ | +> | 寻址模式 | 1 位 | 2 种模式 | +> +> **第二步**:设计寄存器寻址格式 +> +> 两个操作数都是寄存器:操作码 + 模式位 + Rs + Rd = $2 + 1 + 6 + 6 = 15$ 位,剩余 1 位可用于功能扩展或保留。 +> +> **第三步**:设计立即数寻址格式 +> +> 操作码 + 模式位 + Rd + 立即数 = $2 + 1 + 6 + 7 = 16$ 位,恰好填满。7 位立即数可表示 $-128 \sim 127$,满足要求。 +> +> **结果**: +> +> ``` +> 寄存器寻址:[操作码 2 位][模式 1 位][Rs 6 位][Rd 6 位][保留 1 位] +> 立即数寻址:[操作码 2 位][模式 1 位][Rd 6 位][立即数 7 位] +> ``` +> +> **启示**:指令格式设计是一个**位数分配的优化问题**——在固定的指令字长约束下,权衡操作码空间、寄存器数量、寻址模式和立即数范围。 + ## 关联笔记 - [[计算机系统结构/复习文档/计算机系统结构基础与定量原理]] - [[计算机系统结构/复习文档/流水线技术]] diff --git a/计算机系统结构/复习文档/流水线技术.md b/计算机系统结构/复习文档/流水线技术.md index 5b50d0d..4ef0c27 100644 --- a/计算机系统结构/复习文档/流水线技术.md +++ b/计算机系统结构/复习文档/流水线技术.md @@ -62,6 +62,30 @@ graph LR > [!question] 定向传送(旁路)为什么不能完全消除数据冲突? > Load-use 冲突:从内存 Load 的数据在 EX 段结束时才可用,但下一条指令可能需要在 EX 段开始时就使用它,此时必须插入一个气泡。 +**Load-use 冲突 vs Forwarding 效果对比**: + +假设指令序列为 `LW R1, 0(R2)` → `ADD R3, R1, R4`,Load 指令的 R1 在 MEM 段末才从存储器读出,而 ADD 指令在 EX 段开头就需要 R1 的值。 + +| 时钟周期 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +|:--------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +| **无 Forwarding,无 Stall** | | | | | | | | | +| `LW R1` | IF | ID | EX | MEM | WB | | | | +| `ADD R3,R1,R4` | | IF | ID | **EX** | MEM | WB | | | +| **有 Stall,无 Forwarding** | | | | | | | | | +| `LW R1` | IF | ID | EX | MEM | WB | | | | +| `ADD R3,R1,R4` | | IF | ID | **Stall** | EX | MEM | WB | | +| **有 Forwarding(Load-use 需 1 拍 Stall)** | | | | | | | | | +| `LW R1` | IF | ID | EX | MEM | WB | | | | +| `ADD R3,R1,R4` | | IF | ID | **Stall** | EX | MEM | WB | | +| **理想 Forwarding(非 Load 指令)** | | | | | | | | | +| `ADD R1,...` | IF | ID | EX | MEM | WB | | | | +| `ADD R3,R1,R4` | | IF | ID | EX | MEM | WB | | | + +> [!important] 关键区别 +> - **EX→EX 前推**(非 Load 指令):结果在 EX 段末产生,下一条指令 EX 段初可用,**无需 Stall** +> - **MEM→EX 前推**(Load-use):数据在 MEM 段末才可用,但下一条指令 EX 段初就需要,**必须插入 1 拍 Stall** +> - 这就是为什么 Load-use 冲突无法被完全消除的根本原因——**存在一拍的物理时间差** + #### 2.3 控制冲突(Control Hazard) 分支指令的执行结果决定后续取指方向,但在分支确定前已经预取了后续指令。 @@ -74,6 +98,8 @@ graph LR ### 三、流水线调度(非线性流水线) +非线性流水线中,各段的使用存在重叠冲突,不能简单地每拍送入新任务。需要通过**预约表分析**确定安全的调度间隔。 + #### 3.1 预约表 记录一个任务在各时钟周期对各段的占用情况,是调度分析的起点。 @@ -97,14 +123,6 @@ $$C_{new} = SHR^{(j)}(C) \lor C_0$$ 其中 $SHR^{(j)}$ 表示右移 $j$ 位,$\lor$ 表示按位或。 -> [!example] 调度示例 -> 禁止表 $F = \{1, 3, 4, 6\}$,则 $C_0 = 101101$ -> -> - $j=2$:$SHR^{(2)}(101101) \lor 101101 = 001011 \lor 101101 = 101111 = C_1$ -> - $j=5$:$SHR^{(5)}(101101) \lor 101101 = 000001 \lor 101101 = 101101 = C_0$ -> -> 在 $C_1$ 状态:$j=2$ 得 $C_1$,$j=5$ 得 $C_0$ - #### 3.5 最优调度策略 在状态转移图中找出所有**闭合回路**,计算每个回路的平均延迟: @@ -113,13 +131,79 @@ $$\text{平均延迟} = \frac{\text{回路中各间隔之和}}{\text{回路长 平均延迟最小的回路即为最优调度策略。 -| 调度策略 | 平均延迟 | -|:--------:|:--------:| -| (5) | 5 | -| (2, 5) | 3.5 | -| (2, 7) | 4.5 | +> [!example] 完整例题:从预约表到最优调度 +> +> **已知**某非线性流水线预约表如下(3 段:S1, S2, S3,7 个时钟周期): +> +> | 段 \\ 时刻 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +> |:----------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +> | S1 | x | | | | x | | | +> | S2 | | x | | x | | | | +> | S3 | | | x | | | | x | +> +> **Step 1:提取禁止表 F** +> +> - S1 在时刻 1 和 5 被占用,间隔 = 5 - 1 = **4** +> - S2 在时刻 2 和 4 被占用,间隔 = 4 - 2 = **2** +> - S3 在时刻 3 和 7 被占用,间隔 = 7 - 3 = **4** +> +> 因此禁止表 $F = \{2, 4\}$(去掉重复的 4) +> +> **Step 2:构建初始冲突向量 $C_0$** +> +> 最大禁止间隔为 4,冲突向量共 4 位(第 1~4 位),第 $i$ 位为 1 表示间隔 $i$ 禁止: +> +> $$C_0 = 1010$$ +> +> 其中第 2 位和第 4 位为 1(对应禁止间隔 2 和 4),第 1 位和第 3 位为 0(对应允许间隔 1 和 3)。 +> +> **Step 3:计算各状态转移** +> +> 初始状态 $C_0 = 1010$,允许间隔为 $j \in \{1, 3\}$: +> +> - $j=1$:$SHR^{(1)}(1010) \lor 1010 = 0101 \lor 1010 = 1111 = C_1$ +> - $j=3$:$SHR^{(3)}(1010) \lor 1010 = 0001 \lor 1010 = 1011 = C_2$ +> +> 状态 $C_1 = 1111$,所有位为 1,不允许任何间隔,为**终态**。 +> +> 状态 $C_2 = 1011$,第 2 位为 0,允许间隔 $j=2$: +> +> - $j=2$:$SHR^{(2)}(1011) \lor 1010 = 0010 \lor 1010 = 1010 = C_0$ +> +> **Step 4:画状态转移图** +> +> ```mermaid +> graph LR +> C0["C0 = 1010"] -->|"j = 1"| C1["C1 = 1111"] +> C0 -->|"j = 3"| C2["C2 = 1011"] +> C2 -->|"j = 2"| C0 +> ``` +> +> **Step 5:找最优调度策略** +> +> 从 $C_0$ 出发,找到所有闭合回路: +> +> | 回路 | 间隔序列 | 平均延迟 | +> |:----:|:--------:|:--------:| +> | $C_0 \xrightarrow{j=3} C_2 \xrightarrow{j=2} C_0$ | (3, 2) | $\frac{3+2}{2} = 2.5$ 拍 | +> +> 这是唯一可循环的回路,**最优调度策略为 (3, 2)**,平均延迟 **2.5 拍**。 +> +> > [!tip] 验证 +> > 按 (3, 2, 3, 2, ...) 调度,任务进入时刻为 0, 3, 5, 8, 10, 13, ... +> > 每个任务 7 拍完成,5 个任务在时刻 0~17 完成,吞吐率 = $5/18 \approx 0.278$(远优于串行的 $1/7 \approx 0.143$) -最优策略为 **(2, 5)**,平均延迟 3.5 拍。 +**状态转移图示例**(与上面例题对应的图示化表达): + +```mermaid +graph LR + C0["C0 = 1010"] -->|"j = 1"| C1["C1 = 1111 (终态)"] + C0 -->|"j = 3"| C2["C2 = 1011"] + C2 -->|"j = 2"| C0 +``` + +> [!question] 为什么 C1 = 1111 是"死胡同"? +> 当所有位都为 1 时,没有任何间隔是允许的——此时流水线已完全阻塞,新任务无法进入。因此在实际调度中应**避免进入此类状态**,本题的最优回路 (3,2) 恰好避开了 C1。 ### 四、时空图分析 @@ -132,6 +216,82 @@ $$\text{平均延迟} = \frac{\text{回路中各间隔之和}}{\text{回路长 3. 注意数据相关的约束(无定向传送时需插入气泡) 4. 计算总完成时间和吞吐率 +> [!example] 数值化示例:4 段流水线性能分析 +> +> **已知**:4 段流水线(S1, S2, S3, S4),各段延迟分别为 2ns, 4ns, 3ns, 2ns。时钟周期取最长段延迟 = 4ns。连续执行 5 个任务。 +> +> **情况 A:线性流水线,无冲突** +> +> | 任务 \\ 段 | S1(2ns) | S2(4ns) | S3(3ns) | S4(2ns) | 完成时刻 | +> |:----------:|:-------:|:-------:|:-------:|:-------:|:--------:| +> | T1 | 0-4 | 4-8 | 8-12 | 12-16 | 16 | +> | T2 | 4-8 | 8-12 | 12-16 | 16-20 | 20 | +> | T3 | 8-12 | 12-16 | 16-20 | 20-24 | 24 | +> | T4 | 12-16 | 16-20 | 20-24 | 24-28 | 28 | +> | T5 | 16-20 | 20-24 | 24-28 | 28-32 | 32 | +> +> - 总时间 $T_k = (4-1) \times 4 + (2+4+3+2) = 12 + 11 = 23$ ns(但按 4ns 时钟周期计算 = 32ns) +> - 串行时间 $T_{seq} = 5 \times (2+4+3+2) = 55$ ns +> - 吞吐率 $TP = 5/32 = 0.156$ 任务/ns +> - 加速比 $S = 55/32 = 1.72$ +> - 效率 $E = 1.72/4 = 43\%$ +> +> > [!question] 为什么效率只有 43%? +> > 因为各段延迟不均衡(S2 占 4ns,S1 和 S4 只占 2ns),S1 和 S4 的大部分时间处于空闲状态。这就是**段间不均衡**带来的效率损失——改善方法是**细分较长段**或**合并较短段**。 + +> [!example] 综合计算题:含数据冲突的流水线性能 +> +> **已知**:5 段流水线(IF/ID/EX/MEM/WB),每段 1 时钟周期。执行以下指令序列: +> +> ``` +> I1: LW R1, 0(R2) ; Load R1 from memory +> I2: ADD R3, R1, R4 ; R3 = R1 + R4 (RAW on R1, Load-use) +> I3: SUB R5, R3, R6 ; R5 = R3 - R6 (RAW on R3) +> I4: SW R5, 0(R7) ; Store R5 to memory (RAW on R5) +> ``` +> +> **问题**:(a) 无 Forwarding 时需要多少 Stall?(b) 有 Forwarding 时需要多少 Stall?(c) 分别计算加速比和效率。 +> +> **(a) 无 Forwarding** +> +> | 时钟 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | +> |:----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:--:|:--:|:--:| +> | I1 LW | IF | ID | EX | MEM | WB | | | | | | | | +> | I2 ADD | | IF | ID | **S** | **S** | EX | MEM | WB | | | | | +> | I3 SUB | | | IF | **S** | **S** | ID | **S** | **S** | EX | MEM | WB | | +> | I4 SW | | | | | | IF | **S** | **S** | ID | **S** | **S** | EX... | +> +> 解释:I2 等 I1 在 WB 后才能读 R1(需等 2 拍);I3 等 I2 在 WB 后才能读 R3;I4 等 I3 在 WB 后才能读 R5。 +> +> 无 Forwarding 总共需要 **8 个 Stall 拍**。 +> +> **(b) 有 Forwarding(Load-use 需 1 拍 Stall)** +> +> | 时钟 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +> |:----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +> | I1 LW | IF | ID | EX | MEM | WB | | | | +> | I2 ADD | | IF | ID | **S** | EX | MEM | WB | | +> | I3 SUB | | | IF | ID | EX | MEM | WB | | +> | I4 SW | | | | IF | ID | EX | MEM | WB | +> +> 解释: +> - I2 的 R1 依赖 I1(Load-use):数据在 MEM 末可用,I2 需在 EX 初使用,插入 **1 拍 Stall** +> - I3 的 R3 依赖 I2(非 Load,EX→EX 前推):**无需 Stall** +> - I4 的 R5 依赖 I3(非 Load,EX→EX 前推):**无需 Stall** +> +> 有 Forwarding 仅需 **1 个 Stall 拍**。 +> +> **(c) 性能对比** +> +> | 指标 | 无 Forwarding | 有 Forwarding | +> |:----:|:------------:|:-------------:| +> | 总拍数 | 12 拍 | 8 拍 | +> | 串行时间 | $4 \times 5 = 20$ 拍 | 20 拍 | +> | 加速比 | $20/12 \approx 1.67$ | $20/8 = 2.5$ | +> | 效率 | $1.67/5 = 33.3\%$ | $2.5/5 = 50\%$ | +> +> Forwarding 将加速比提升了 **50%**(从 1.67 到 2.5),这就是为什么现代处理器都实现了 Forwarding 旁路网络。 + ## 关联笔记 - [[计算机系统结构/复习文档/计算机系统结构基础与定量原理]] - [[计算机系统结构/复习文档/存储系统与Cache]] diff --git a/计算机系统结构/复习文档/计算机系统结构基础与定量原理.md b/计算机系统结构/复习文档/计算机系统结构基础与定量原理.md index ff2cbf3..80e7d62 100644 --- a/计算机系统结构/复习文档/计算机系统结构基础与定量原理.md +++ b/计算机系统结构/复习文档/计算机系统结构基础与定量原理.md @@ -73,6 +73,36 @@ $$S = \frac{1}{(1-f) + \frac{f}{n}}$$ > [!question] 某功能占系统时间 20%,提升 15 倍,加速比是多少? > $S = \frac{1}{0.8 + 0.2/15} = \frac{1}{0.8133} \approx 1.23$。即使将该功能提升到无限快,加速比上限也只有 $1/0.8 = 1.25$。 +> [!example] 综合例题:多部件同时改进 +> +> **题目**:某系统由三个部件组成,执行时间占比及改进方案如下: +> +> | 部件 | 原始时间占比 | 改进后加速倍数 | +> |:----:|:----------:|:-------------:| +> | A | 40% | 3 倍 | +> | B | 35% | 2 倍 | +> | C | 25% | 不改进 | +> +> 求系统总加速比。 +> +> **解题步骤**: +> +> **第一步**:确定各部件改进后的时间比例 +> +> 改进后,各部件在总时间中的占比变为: +> +> $$T_{new} = (1-f_A-f_B-f_C) + \frac{f_A}{n_A} + \frac{f_B}{n_B} + \frac{f_C}{n_C}$$ +> +> **第二步**:代入数值 +> +> $$T_{new} = 0 + \frac{0.40}{3} + \frac{0.35}{2} + \frac{0.25}{1} = 0.1333 + 0.175 + 0.25 = 0.5583$$ +> +> **第三步**:计算加速比 +> +> $$S = \frac{1}{T_{new}} = \frac{1}{0.5583} \approx 1.79$$ +> +> **第四步**:分析——如果只改进部件 A(占比最大),加速比为 $1/(0.6+0.4/3) = 1.67$;如果只改进部件 B,加速比为 $1/(0.65+0.35/2) = 1.24$。同时改进 A 和 B 才达到 1.79,说明**改进占比大且加速倍数高的部件收益最大**。 + #### 3.3 CPU 性能公式 $$CPU 时间 = IC \times CPI \times \tau$$ @@ -91,6 +121,31 @@ $$CPU 时间 = IC \times CPI \times \tau$$ > [!warning] MIPS 的局限性 > 不同指令集的 MIPS 不可直接比较。RISC 机器 MIPS 通常高于 CISC,但不代表性能更优——RISC 需要更多指令完成同样任务。 +> [!example] 例题:两种方案的 CPI 与执行时间对比 +> +> **题目**:同一程序在两种处理器上运行,时钟频率均为 2 GHz,相关参数如下: +> +> | 参数 | 方案 X | 方案 Y | +> |:----:|:------:|:------:| +> | 指令条数 IC | 50 亿条 | 10 亿条 | +> | 平均 CPI | 1.2 | 5.0 | +> +> 哪个方案更快?快多少? +> +> **解**: +> +> **方案 X**: +> +> $$T_X = IC \times CPI \times \tau = 5 \times 10^9 \times 1.2 \times \frac{1}{2 \times 10^9} = 3.0 \text{ 秒}$$ +> +> **方案 Y**: +> +> $$T_Y = 1 \times 10^9 \times 5.0 \times \frac{1}{2 \times 10^9} = 2.5 \text{ 秒}$$ +> +> 方案 Y 更快,加速比为 $T_X / T_Y = 3.0 / 2.5 = 1.2$ 倍。 +> +> **启示**:虽然方案 X 的 CPI 只有 1.2(远低于方案 Y 的 5.0),但方案 Y 的指令条数只有方案 X 的 1/5。这说明**不能只看 CPI 或 IC 单一指标,必须综合考虑三者的乘积**。这也解释了为什么 CISC(IC 少但 CPI 高)和 RISC(IC 多但 CPI 低)可以达到相当的性能水平。 + #### 3.4 程序局部性原理 程序在执行时所访问的地址空间分布不是随机的,而是**相对地聚集**。 @@ -120,6 +175,22 @@ graph LR | MFLOPS | 浮点操作数 / $(T \times 10^6)$ | 科学计算 | | 加速比 | $T_{old} / T_{new}$ | 优化前后对比 | +> [!tip] CPU 性能公式的三因素关系 +> CPU 执行时间由三个因素共同决定,优化任何一个因素都能缩短执行时间,但效果取决于瓶颈所在: + +```mermaid +graph TD + T["CPU Time"] --> IC["IC, Instruction Count"] + T --> CPI["CPI, Cycles Per Instruction"] + T --> tau["tau, Clock Period"] + IC --> C["Compiler"] + IC --> ISA["ISA Design"] + CPI --> Pipe["Pipeline"] + CPI --> Cache["Cache Hit Rate"] + tau --> Tech["Process Technology"] + tau --> Logic["Logic Design"] +``` + ## 关联笔记 - [[计算机系统结构/复习文档/流水线技术]] - [[计算机系统结构/复习文档/存储系统与Cache]]