Appearance
题目
假定编译器将赋值语句 "x=x+3;" 转换为指令 "add xaddr, 3",其中 xaddr 是 x 对应的存储单元地址。若执行该指令的计算机采用页式虚拟存储管理方式,并配有相应的 TLB,且 Cache 使用直写(Write Through)方式,则完成该指令功能需要访问主存的次数至少是( )。
错因
A
把"直写法在 Cache 命中时不访问主存"当成默认——这是对直写的根本误解。直写的定义就是:每次写都同时更新 Cache 和主存,命中也好不命中也好,主存写一定发生。如果命中也不写主存,那叫回写法(Write Back)——题目特意说了直写,就是要考这一点。
C
把"读 x"也算成了 1 次主存访问。最少情况下假设 Cache 命中(题目问"至少",要按最佳情况推),读 x 直接从 Cache 拿,不访存。错答 C 的人没区分"读取" vs "命中 Cache 的读取"——前者在概念上是从存储器读,后者实际只摸了 Cache。
D
把"取指 + 读 x + 写 x"三步全都算成访存。取指在最佳情况下也命中 Cache,不访存;读 x 同理。只有直写写入这一步躲不掉。把每一步都默认成访存,相当于把"虚拟存储 + Cache + TLB"这一整套加速机制视为不存在——这正是题目要暴露的常见误解。
总解析
核心思路:题目问"至少",意思是按最佳命中情况推算。
执行 add xaddr, 3 等价于 M[xaddr] ← M[xaddr] + 3,把它拆成 CPU 实际做的步骤,对每一步分别判断"在最佳情况下是否访存"。
第一步:列出 CPU 实际执行的动作
| 步骤 | 操作 | 用到的资源 |
|---|---|---|
| 1. 取指 | 从 PC 指向的地址取出 add xaddr, 3 | TLB(页表项) + Cache(指令) |
| 2. 读 x | 从 xaddr 读出 x 的当前值 | TLB(页表项) + Cache(数据) |
| 3. 计算 | 在 ALU 中算 x + 3 | 不涉及主存 |
| 4. 写 x | 把新值写回 xaddr | TLB + Cache + 主存(看写策略) |
第二步:在"最佳命中"假设下,每步是否需要访问主存
| 步骤 | TLB 命中? | Cache 命中? | 是否访存 |
|---|---|---|---|
| 取指 | 是 | 是 | 否(指令在 Cache 里) |
| 读 x | 是 | 是 | 否(x 在 Cache 里) |
| 计算 | — | — | 否(纯 ALU) |
| 写 x(直写) | 是 | 是 | 是 ⚠️ |
第三步:直写法的核心约定
直写(Write Through):写命中时,同时写 Cache 和主存;写不命中时,看写分配策略。
无论命中与否,主存一定会被写一次——这是直写法相对回写法的唯一代价(也是它"省掉脏位 + 替换时不需写回"的来源)。所以即使所有 Cache 都命中、所有 TLB 都命中,写 x 这一步逃不掉的 1 次主存访问。
第四步:汇总
最终答案是 B(1)。
关键速记:
| 写策略 | 写命中时是否一定写主存 | 是否需要 dirty 位 |
|---|---|---|
| 直写 (Write Through) | 是(每次都写) | 不需要 |
| 回写 (Write Back) | 否(只更新 Cache,等替换时才写回) | 需要 |
题面陷阱:
- "TLB" 是地址翻译加速器,TLB 命中 = 不必去主存查页表——这步主存访问被省掉
- "Cache 命中 = 不必去主存取/查数据"——这步也省掉
- 但直写这个机制是个特例:哪怕一切命中,写主存这一刀躲不开