简介:张晨曦版《计算机系统结构》课后答案是一份面向高校计算机专业学生、自学者和考研备考者的复习资料,围绕教材第 1 章“计算机系统结构的基本概念”展开。文档系统梳理了层次机构、虚拟机、翻译与解释、计算机系统结构、计算机组成与实现、Amdahl 定律、程序的局部性原理、CPI、并行性等核心术语的含义,并在此基础上补充了 Flynn 分类法、系统设计中经常使用的定量原理,帮助读者厘清抽象概念之间的联系,搭建完整的知识框架。对课后习题也给出较详细的解答,例如根据指令类型、执行数量和平均周期数计算有效 CPI、MIPS 及程序执行时间,并展示具体代入过程;同时覆盖存储程序计算机、系列机、软件兼容、兼容机、模拟与仿真等多类基础概念题,适合需要对照练习、查漏补缺的学习场景。资源包为 1 个 doc 文档,大小约 170KB,目前已有 1379 人学习/下载,是备考和日常复习时较实用的辅助参考资料。
1. 张晨曦版《计算机系统结构》课后答案练的到底是什么
搜索「计算机系统结构 张晨曦 课后答案」的人,多半是算出一个数却不敢确认。这类题目的答案几乎都是“一个数”:流水线吞吐率、Cache 命中率、向量执行拍数,参数错一个,结果就差一个数量级。这套教材的习题体系很固定,基本可分五类:流水线的时空图与加速比、存储层次的地址映射与替换策略、向量处理的启动开销、指令级并行调度、多处理机一致性协议。把这几类的计算路径练熟,比背标准答案有用。
后续内容按这五类展开,每类给出公式、手算步骤和验证脚本;正在做作业、备考的读者可以直接照着改数套用,已经工作的人也能把这套“先定公式、再画图、最后验证”的思路迁移到线上性能分析场景。
2. 流水线课后题:时空图、数据相关与分支损失的计算路径
流水线一章的课后题基本三种:给段数和任务数算性能三件套;给指令序列判断相关并数停顿;结合分支预测算平均损失。多数人卡在第二种,因为时空图一画错,后面的数字全错。先统一两个习惯:所有公式从总时间出发;画图前先确认题目给没给转发通路,转发有无直接决定停几拍。
2.1 吞吐率、加速比、效率:统一从总时间公式出发
均匀 k 段流水线完成 n 个任务的理想总时间是 T = (k+n−1)·Δt,Δt 是每段执行时间。性能三件套都从这个总时间派生出来,考试里最常见的做法也是先把 T 算对,再套表里的三个式子。
| 指标 | 公式 | 极限 |
|---|---|---|
| 吞吐率 TP | TP = n / ((k+n−1)·Δt) | n→∞ 时趋近 1/Δt |
| 加速比 S | S = k·n / (k+n−1) | n→∞ 时趋近 k |
| 效率 E | E = n / (k+n−1) | n→∞ 时趋近 1 |
一道典型题:4 段流水线,每段 10 ns,连续执行 100 条指令。T = (4+100−1)×10 = 1030 ns,TP ≈ 0.0971 条/ns,S = 400/103 ≈ 3.883,E ≈ 0.971。这几个数不建议口算,用一段短脚本替换参数最稳妥:
def pipeline_metrics(k, dt, n):
total = (k + n - 1) * dt
tp = n / total
s = n * k * dt / total
e = s / k
return total, tp, s, e
total, tp, s, e = pipeline_metrics(4, 10, 100)
print(f"总时间 {total} ns, 吞吐率 {tp:.4g} 条/ns, 加速比 {s:.4f}, 效率 {e:.4f}")
total 是计算的核心骨架,(k+n−1)Δt 覆盖了“第一条指令充满流水线的 k−1 拍”加“剩余 n 条指令逐条流出的 n·Δt 拍”。k 是段数,dt 是每段用时,n 是任务数或指令条数;三个返回值里吞吐率的单位是“条/时间单位”,加速比和效率是无量纲比值。作业改数据时只动 k、dt、n 三个参数即可。
若题目特别说明“各段时间不相等”,公式要换成基于瓶颈段:先找出最大的 Δt_max,总时间近似取 (k+n−1)·Δt_max。很多学生直接忽略“瓶颈段”三个字,用平均段长算,结果与标准答案差出一个数量级,这是这章最典型的失分点。
提示:做题前先看题目有没有给出“转发”或“forwarding”字样。有转发和没转发,同一道题的停顿周期最多能差 3 拍。
2.2 数据相关冲突:把指令一格格排进五级时空图
判断数据相关只盯两个时刻:源指令在哪一拍产生结果,目标指令在哪一拍需要这个结果。假设经典五段流水线 IF、ID、EX、MEM、WB:
- I1: ADD R1, R2, R3,R1 在 EX 段结束(第 3 拍末)即可用,WB 段第 5 拍才写回寄存器堆。
- I2: SUB R4, R1, R5,ID 段第 3 拍读寄存器,EX 段第 4 拍真正使用 R1。
有转发通路时,I1 的 EX 结果直接从 EX/MEM 锁存器送到 I2 的 ALU 输入,I2 不需要停顿。没有转发时,I2 必须等 I1 写回,EX 段推迟到第 6 拍,中间多出 2~3 个气泡,具体拍数看教材的数据通路假设。
Load-use 冲突是考试频率最高的特例。I1: LW R1, 0(R2),数据在第 4 拍末才从访存返回;I2: ADD R3, R3, R1 原定第 4 拍用 ALU,此时数据还没到,即使有转发也必须停 1 拍。做题时把两条指令的占用格画在同一张时空图上,数水平方向的气泡格,一个是一个,不要凭感觉估。
画图建议:横轴是周期,纵轴是五段,每格填指令编号。先画 I1 的对角线,再画 I2,两线之间出现空行的位置就是要插气泡的位置。依赖链越长,图越好画,因为每对消费关系的偏移量都是固定值。
2.3 分支损失与预测:按概率加权平均每条指令的停顿
分支相关的计算套路是把损失折算到每条指令头上:CPI_stall = f_branch × L,f_branch 是分支指令占比,L 是每次分支的平均损失。带预测时 L 按概率加权,L = p_correct × L_correct + p_wrong × L_wrong。
一套常见的题设:分支指令占 20%,无预测时每次分支损失 3 拍;引入预测后,预测正确率 80%,正确时仍损失 1 拍(分支结果确定前流水线不能继续取后续指令),错误时损失 4 拍。则 L = 0.8×1 + 0.2×4 = 1.6 拍,CPI_stall = 0.2×1.6 = 0.32,理想 CPI=1 的流水线实际 CPI = 1.32。对比完全不预测的 1 + 0.2×3 = 1.6,预测的价值被显式算成了 0.28。
这题最容易犯的错误是忘记“预测正确也要付 1 拍代价”。很多教材把“分支开销”定义为从取指被阻塞到确定方向的周期数,预测正确而代价为 0 是理想模型,不是默认模型。既然题目明确给了正确时损失 1 拍,就必须按加权公式算,不能图省事忽略。
3. 存储系统课后题:Cache 拆位、LRU 命中率与 TLB 平均访存时间
存储系统的题型最固定,也最不该失分,但细节最多:地址按字节还是按字、组相联度有没有乘进去、LRU 命中后有没有刷新顺序。这一章的解法高度模板化,按“先拆位、再填表、最后算时间”三步走,基本不会跑偏。
3.1 Cache 地址拆位:先定块内偏移,再定索引,剩下全是标记
给定主存地址位数 A、Cache 容量 C、块大小 B、相联度 W,标准拆法:块内偏移位数 = log2(B);组数 = C / (B×W),索引位数 = log2(组数);标记位数 = A − 偏移位数 − 索引位数。三步里只要错一步,后面所有题全错,所以建议记住一张对照表:
| 映射方式 | 索引位数 | 标记位数 | 命题特点 |
|---|---|---|---|
| 直接映射 | log2(C/B) | A − log2(C/B) − log2(B) | 冲突最高,硬件最省 |
| W 路组相联 | log2(C/(B·W)) | A − log2(C/(B·W)) − log2(B) | 最常见,折中方案 |
| 全相联 | 0 | A − log2(B) | 标记长,比较器成本高 |
一道完整例题:主存地址 32 位,Cache 容量 64 KB,块大小 64 B,4 路组相联。块内偏移 = log2(64) = 6 位;组数 = 64 KB / (64 B × 4) = 256,索引 = 8 位;标记 = 32 − 6 − 8 = 18 位。注意这里最容易错的两点:分母多乘一个 4(组相联度),以及 Cache 容量和块大小没有先统一单位。64 KB 与 64 B 统一成字节后相除是 1024 个块,再除 4 路才是 256 组。
3.2 LRU 命中率手算:按组维护顺序表,命中后必须刷新
给定访存序列算命中率,是所有“课后答案”搜索引擎里点开率最高的一类题。手算的唯一正确姿势是按组画状态表。下面用一个 4 块 2 路组相联的 Cache 演示,块号按地址直接映射到组号,偶数组 0、奇数组 1,访问序列为 0, 1, 2, 3, 0, 1, 4, 0, 1, 2, 3, 4:
| 步骤 | 访问块号 | 所在组 | 组内 LRU 顺序(左旧右新) | 命中/缺失 |
|---|---|---|---|---|
| 1 | 0 | 组0 | 0 | 缺失 |
| 2 | 1 | 组1 | 1 | 缺失 |
| 3 | 2 | 组0 | 0, 2 | 缺失 |
| 4 | 3 | 组1 | 1, 3 | 缺失 |
| 5 | 0 | 组0 | 2, 0 | 命中 |
| 6 | 1 | 组1 | 3, 1 | 命中 |
| 7 | 4 | 组0 | 0, 4(替换2) | 缺失 |
| 8 | 0 | 组0 | 4, 0 | 命中 |
| 9 | 1 | 组1 | 3, 1 | 命中 |
| 10 | 2 | 组0 | 0, 2(替换4) | 缺失 |
| 11 | 3 | 组1 | 1, 3 | 命中 |
| 12 | 4 | 组0 | 2, 4(替换0) | 缺失 |
结果命中 5 次、缺失 7 次,缺失率 7/12 ≈ 58.3%。这张表最有价值的地方是 5、6、8、9、11 这五行的“命中后刷新”:访问 0 命中后,0 必须从旧位置移到最右端,不刷新,下一步就可能把刚命中的块当 LRU 换出去,后续结果全部偏移。手动验算时,每行做完看一眼“左旧右新”是否符合直觉,能挡掉大半低级错误。
3.3 TLB 与页表结合题:三段概率分支各自算时间再加权
涉及 TLB 的题通常要求算平均访存时间。处理顺序固定:TLB 命中,直接拿物理地址访存;TLB 缺失则查页表;页表也没有就要进缺页处理。三个分支的概率之和为 1,平均时间就是三段时间的概率加权。
t_tlb, t_mem, t_page = 1, 100, 3_000_000 # 单位:ns
p_tlb, p_pte = 0.98, 0.99
t1 = t_tlb + t_mem # TLB 命中:查 TLB + 一次访存
t2 = t_tlb + t_mem + t_mem # TLB 缺失:查 TLB + 查页表 + 一次访存
t3 = t_tlb + t_mem + t_page # 缺页:查 TLB + 查页表 + 缺页处理
avg = p_tlb * t1 + (1 - p_tlb) * (p_pte * t2 + (1 - p_pte) * t3)
print(f"平均访存时间约 {avg:.1f} ns")
t2 里包含了“页表也住在主存中”的一次访存,很多解法在这里漏掉一个 t_mem。t3 用 3,000,000 ns 模拟一次缺页换入换出,即使概率只有 1%,对平均时间的贡献仍然达到约 600 ns,这说明 TLB 缺失的代价主要由缺页而不是由 TLB 本身决定。代入本例,平均访存时间约 703 ns,其中约 600 ns 都来自那个 1% 的缺页尾巴,这个反直觉结论经常被拿来考“为什么 TLB 要保留高频页”。
4. 向量处理与指令调度题:启动开销、链接和循环展开的算账方法
向量处理这一章的课后题,本质是“把流水线的账算到向量级”。单条指令的启动开销、多条指令之间的链接重叠、访存部件是否独立,三个变量排列组合出各种数字。指令调度题则换个角度,要求用展开去填补 load-use 造成的空档。这两类题都不难,但符号约定不统一,答案经常对不上。
4.1 向量总时间:按“启动 + 长度”累加,链接只是重叠启动阶段
统一约定:长度为 n 的向量,单条指令完成时间 T = S + n − 1,S 是启动开销(流水线充满的拍数),之后每拍流出一个结果元素。多条向量指令串行执行时直接累加;两条指令若可链接,后一条会在前一条产出第一个元素后就开始,不再等整条向量结束。以一个具体例子把这三种情况算清楚:访存部件启动 12 拍,向量加部件启动 6 拍,向量长度 64,指令序列为 LOAD V1, A;LOAD V2, B;VADD V3, V1, V2。
| 场景 | 计算式 | 总拍数 |
|---|---|---|
| 无链接、单访存端口 | 75 + 75 + 69 | 219 |
| 无链接、双访存端口 | 75 + 69 | 144 |
| 有链接、双访存端口 | 75 + (6−1) | 80 |
无链接单端口时两条装载串行各占 75 拍,加法再占 69 拍,总共 219 拍。双端口下两条装载并行,144 拍。链接条件下,加法第一个周期就能接收逐元素结果,最后一个元素在第 75 拍就绪,加法的 6 段流水线只剩尾部 5 拍需要填充,总共 80 拍。链接相比双端口无链接省了 64 拍,恰好等于向量长度,这个“省一个向量长度”的结论在教材习题里反复出现,可以直接作为口算校验。
4.2 循环展开与指令调度:用空闲槽精确填满 load-use 延迟
指令调度题的得分点是先画依赖链,再找可移动指令。假设五段流水线,ALU 结果可转发,但 load 到使用必须间隔 1 拍。不展开的循环体里,每条 LW 后面紧跟 ADD,每次迭代都要停 1 拍,4 条指令花 5 拍,CPI = 1.25。展开两次后,把第二条 LW 插进第一条 LW 与 ADD 之间的空档:
| 周期 | 展开后的指令 |
|---|---|
| 1 | LW R1, 0(R2) |
| 2 | LW R3, 4(R2) |
| 3 | ADD R5, R5, R1 |
| 4 | ADD R6, R6, R3 |
| 5 | ADDI R2, R2, 8 |
| 6 | BNE R2, R_loop_end, loop |
展开后 6 条指令 6 拍执行完,CPI = 1。核心在于两个展开体彼此无依赖,第二条 LW 正好占据第一条 LW 的 load-use 空档,而原来的停顿周期被指令本身填满。判断“能否再展开一次”的方法是数依赖链:每条 LW→ADD 是长度为 2 的链,链与链之间只要没有寄存器读写重叠,就可以继续展开。寄存器重命名在这里是常见补充手段,题目若给了可用寄存器数,展开次数上限就是寄存器数除以每个展开体占用的临时寄存器数。
4.3 用可复算脚本统一三种约定
向量题最麻烦的是各教材对“启动开销”的记法不一致,有的用 S+n−1,有的直接写 S+n。手算容易混,写成参数化脚本反而清楚:
def vector_time(startups, n, chained=False):
total = sum(s + n - 1 for s in startups)
if chained:
total -= n * (len(startups) - 1)
return total
print(vector_time([12, 6], 64)) # 装载 + 加法,不链接:144
print(vector_time([12, 6], 64, chained=True)) # 装载 + 加法,链接:80
print(vector_time([12, 12, 6], 64)) # 两条装载 + 加法,串行:219
startups 是各指令启动开销列表,n 是向量长度,chained 表示整条链逐元素可链接。链接时减去 n×(指令数−1),对应“每对相邻指令重叠一个向量长度”。若题目给的链接条件只重叠部分启动开销,把 n 换成题目指定的重叠拍数即可。这个脚本的价值在于把公式和数值分离:改数据重跑,而不是每次从头推。
5. 核对课后答案的三个兜底技巧:极限代入、量纲和反推
作业题算完总觉得不对时,不需要重新整题计算,用三个针对性检查就能定位错在哪一步。
5.1 极限代入:把 n 换成 1 和 2 看结果是否合理
任何带任务数 n 的公式,先代入边界。流水线 T = (k+n−1)Δt,n=1 时 T = k·Δt,含义是一条指令走完 k 段,合理;若代出 T 小于启动开销或者出现负时间,公式必然抄错。Cache 命中率同理,访问序列长度为 1 时命中率只能是 0 或 1,算出小数就是 LRU 表填写顺序错了。这个技巧对付“公式记混”最有效,耗时不到半分钟。
5.2 量纲统一:拍、ns、条/拍先换到同一单位
张晨曦版习题里时间单位交替出现:流水线用拍,Cache 平均访存用 ns,缺页用 ms。混用是最隐蔽的错误来源。3 ms 的缺页时间写成 3,000 ns,平均访存时间会从约 703 ns 掉到不足 300 ns,结论完全翻转。另一处高频量纲错是吞吐率:0.0971 条/ns 换算成 9.71×10^7 条/s,中间差 10 的幂;用的时候要盯住“每纳秒”还是“每秒”。
5.3 反推中间量:把结果代回公式还原关键过程
加速比 S=3.883 代回 S = n·k·Δt / T,反解 T 应得 1030 ns,与题设对得上,说明总时间那一步没问题;对不上就把时空图重新画一遍。LRU 题反推时先核对组号映射,例如直接映射下块号 12 应落到第 12 mod 组数 组,索引位数错一位,状态表从第二行就开始偏。反推时优先对照“命中是否发生在满组”“替换是否选中了最旧的那一路”这两个中间状态,命中率手算题八成以上的返工都集中在组号对应关系上。
222



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



