乱序执行、缓存与预取

共 46 题
📑 题目列表 46 题
#
★★★

1. 为何分支预测失败的惩罚(misprediction penalty)是 OoO 设计的关键约束?

为何分支预测失败的惩罚(misprediction penalty)是乱序执行(OoO)设计的关键约束?

  • 分支误预测惩罚
  • 流水线深度
  • OoO 设计约束

分支误预测惩罚 = 从预测错误到确认错误、再到冲刷流水线重取正确指令所浪费的周期数,正比于流水线的深度(取指到分支执行的距离)。现代高性能 CPU 流水线很深(十几级),误预测惩罚可达 15-30 个周期,意味着一次误预测白费大量已取指/译码/执行的指令。对 OoO 设计而言,投机执行(speculative execution)会沿预测路径取指并执行大量指令,一旦误预测,这些工作全部作废,轻则浪费吞吐,重则引发安全隐患(如 Spectre)。因此分支预测器的准确率直接决定 OoO 性能上限,误预测惩罚是设计时最关键的权衡——预测器必须达到极高准确率(>95%),否则深流水线的收益被误预测吞掉。

误预测惩罚随流水线加深而增大,是 OoO 和投机执行的核心天敌。预测准确率与惩罚的权衡决定了 OoO 处理器能发挥的性能上限。

#
★★★

2. 给定两段循环 RMW 写同一寄存器,说明为何需重命名?

给定两段循环中 RMW(读-改-写)写同一寄存器,说明为何需要寄存器重命名?

  • RMW 依赖
  • 寄存器重命名
  • 假依赖

循环中多次 RMW 写同一寄存器(如 sum += x[i] 反复写 sum 寄存器)会生成 WAW(写后写)和 WAR(读后写)假依赖。例如指令 i 写 r1、指令 i+1 读 r1 又写 r1,若直接用同一物理寄存器,各次迭代的 RMW 串行化,无法并行。寄存器重命名把每个逻辑寄存器映射到不同的物理寄存器(如 r1 的先后值映射到物理 p5、p6、p7),使各次迭代的 RMW 使用不同物理寄存器,消除了 WAW/WAR 假依赖,重叠执行(如第 i 次写物理 p5 的同时,第 i+1 次读物理 p5 已就绪,可并行)。这样循环的 RMW 能流水线化,提升 ILP。

RMW 写同一寄存器产生假依赖(WAR/WAW),阻塞并行。重命名通过物理寄存器隔离不同代的值,消除假依赖,是 OoO 并行化的关键。

#
★★★

3. 解释 ROB 满时新指令必须 stall 的机制?

解释 ROB 满时新指令必须 stall 的机制?

  • ROB 容量
  • 分配限制
  • 冒泡

ROB(Reorder Buffer,重排序缓冲)是按程序顺序记录指令的环形缓冲,每条指令在译码/分配时在 ROB 中占据一项,直到提交(commit)时释放。ROB 容量有限,当 ROB 满时,新指令无法分配 ROB 项,必须 stall(暂停取指/分配),直到最老指令提交并从 ROB 中释放空间。这是 ROB 作为"按序提交"约束导致的资源限制:即使执行单元空闲,ROB 满也会阻塞新指令进入,形成"head-of-line"瓶颈。ROB 大小因此决定 OoO 窗口内能容纳多少条未提交指令,是关键的微架构资源。

ROB 满 stall 是资源限制导致的取指停顿。ROB 大小决定乱序窗口深度,是衡量 OoO 处理器能力的关键指标(如 Intel 的 ROB 有 352 项)。

#
★★★

4. 解释 ROB(Reorder Buffer)在指令提交(commit)阶段的角色?

解释 ROB(Reorder Buffer)在指令提交(commit)阶段的角色?

  • ROB 提交
  • 按序提交
  • 精确状态

ROB 在指令提交(commit)阶段扮演"按序提交"的角色:指令虽然乱序执行完成,但必须在 ROB 中按程序顺序(最老到最新)提交。提交时,指令的结果才真正成为"可见"的架构状态(写回/更新寄存器、内存、是否异常)。ROB 保证两条关键语义:一是按序提交,即使执行乱序,外部观察到的状态变化仍按程序顺序发生,保证精确中断/异常;二是投机执行安全,预测错误时,ROB 中未提交的投机指令被丢弃(冲刷),不产生可见副作用。ROB 是连接"乱序执行"与"有序架构状态"的桥梁。

ROB 的核心是"乱序执行、按序提交"。它让乱序执行的副作用仅在提交时显现,从而保证精确异常与投机执行安全。这是 OoO 处理器的基石。

#
★★★

5. 解释乱序执行(OoO)的本质,保留站、寄存器重命名、ROB 三件套如何配合?

解释乱序执行(OoO)的本质:保留站、寄存器重命名、ROB 三件套?

  • 保留站
  • 寄存器重命名
  • ROB

乱序执行(OoO)的核心由三件套组成:保留站(reservation station)负责动态调度——指令译码后进入保留站等待操作数就绪,就绪即可发射执行,从而按数据可用性而非程序顺序执行;寄存器重命名消除 WAR/WAW 假依赖,让不同代的逻辑寄存器映射到不同物理寄存器,扩大并行性;ROB(重排序缓冲)按程序顺序记录指令并提交,保证乱序执行但按序提交、精确异常与投机安全。三者配合:保留站提供乱序调度,重命名消除假依赖,ROB 保证最终有序,共同实现"乱序执行、按序提交"的高性能模型。

OoO 三件套是理解现代超标量处理器的核心框架。保留站+重命名实现并行,ROB 保证正确性。缺一不可。

#
★★★

6. 解释寄存器重命名如何消除 WAW/WAR 假依赖?

解释寄存器重命名如何消除 WAW/WAR 假依赖?

  • 逻辑/物理寄存器
  • 重命名映射
  • 假依赖消除

寄存器重命名维护一张"逻辑寄存器 → 物理寄存器"的映射表(RAT)。每条写逻辑寄存器的指令,分配一个新的物理寄存器,并把映射更新为指向它。这样,同一逻辑寄存器的先后写入对应不同的物理寄存器,即:WAR(写后读)中,写操作写到新物理寄存器,不影响先前读指令仍在读的旧物理寄存器,反依赖消除;WAW(写后写)中,两次写分配到不同物理寄存器,输出依赖消除。只有 RAW(读操作数依赖真正由前一条指令产生)仍保留,因为读必须等对应物理寄存器就绪。重命名把"名字冲突"转化为"名字无关",让指令按数据流并行。

重命名把逻辑名解耦为物理名,使 WAR/WAW 的"名字冲突"消失,只保留 RAW 数据流依赖。这是消除假依赖、扩大 ILP 的机制。

#
★★★

7. 解释为何 ARM Cortex-A77 扩大乱序执行资源(乱序窗口/ROB 128→160 项)能提升 ILP?

解释为何 ARM Cortex-A77 扩大乱序执行资源(乱序窗口/ROB 由 128 项扩大到 160 项)能提升 ILP?

  • 乱序执行资源(ROB 128→160 项)
  • 乱序窗口
  • 性能提升

PRF(Physical Register File,物理寄存器文件)是存物理寄存器值的硬件。重命名会随每条写指令消耗物理寄存器,物理寄存器/ROB 项越多,可同时处理的在飞行指令越多,乱序执行窗口(OoO window)越大,ILP(指令级并行)更高,能更好掩盖长延迟(如内存访问、缓存缺失)。Cortex-A77 这一代公开资料(AnandTech 深度解析的中文转述等)记载的扩容主要是乱序执行窗口/ROB 由 128 项扩大到 160 项(增加 25%),目的正是容纳更多已重命名/未提交的指令、加深乱序,是 A77 相比 A76 性能提升的关键之一。注:题面所称"整数 PRF 从 128 项扩大到 256 项"的具体数值在本次核验中未能证实(可查证的转述资料均记载为窗口/ROB 128→160,未见 A77 PRF=256 项的说法),此处按可证实口径修正;但"更多物理寄存器/ROB 项 = 更大重命名与乱序深度 = 更高 ILP"的原理不变。

物理寄存器/ROB 数量是 OoO 深度的资源基础。更多项 = 更大乱序窗口 = 更高 ILP,能掩盖更长的延迟。这是微架构权衡面积与性能的典型。

#
★★★

8. 解释 scoreboard 算法中指令等待、执行、写回的三个阶段?

解释 scoreboard 算法中指令等待、执行、写回的三个阶段?

  • 记分板
  • 指令状态
  • 流水线调度

scoreboard(记分板)是 CDC 6600 提出的最早动态调度算法,记录指令的四个状态:读操作数(Issue)、执行(Execute)、写结果(Write Result)。核心细节:Issue 阶段分配资源并检查真正的结构冲突(无 WAR,因为原始记分板不处理 WAR);Read Operands 阶段等待操作数就绪(无 RAW 冲突);Execute 阶段计算并监视写冲突(避免 WAW);Write Result 阶段写回(若后续指令可能读被覆盖的寄存器,则等待)。记分板通过追踪操作数就绪状态,让指令在数据就绪时执行,实现乱序完成,但受限于单总线写回(无 CDB 广播),且需等待写冲突。它是乱序执行的先驱,但被 Tomasulo 的重命名改进。

scoreboard 是动态调度的鼻祖,通过控制指令的 Issue/Execute/Write 阶段避免结构冲突、RAW/WAW。它没有重命名,处理 WAR 较保守,后来被 Tomasulo 的保留站+CDB 取代。

#
★★★

9. 给定一段 for(i=0;i<n;i++){...} 循环的分支指令,2-bit 预测器工作过程?

给定一段 for(i=0;i<n;i++){...} 循环的分支指令,解释 2-bit 预测器的工作过程?

  • 2-bit 计数器
  • 分支预测
  • 循环行为

2-bit 预测器用 2 位饱和计数器(4 个状态:00 强不取、01 弱不取、10 弱取、11 强取)跟踪每个分支的历史。循环分支"i<n 时循环、i>=n 时退出":多数迭代预测"取"(保持 11 强取),仅最后一次迭代实际"不取"导致一次误预测(从 11 降到 10),下一次循环重新迭代时又从 10 升回 11。因此对 n 次循环,2-bit 预测器只在最后一次迭代误预测一次,其余 n-1 次都正确,误预测率约 1/n。相比 1-bit 预测器,2-bit 在交替分支上更稳定,不易抖动。

2-bit 饱和计数器对"多数偏向"的分支(如循环)几乎完美,只在方向改变时误预测一次。循环分支的稳定行为使 2-bit 预测器准确率极高。

#
★★★

10. 解释 1-bit 与 2-bit saturating counter 分支预测器的差异?

解释 1-bit 与 2-bit saturating counter 分支预测器的差异?

  • 1-bit 预测器
  • 2-bit 预测器
  • 抗抖动

1-bit 预测器只记录上次分支是否被取(1 位状态),预测"与上次相同"。它对规律分支(如多次取)有效,但对"取/不取交替"的分支会在每次转移时误预测(性能差)。2-bit 饱和计数器用 2 位表示 4 个状态(强取/弱取/弱不取/强不取),需要连续两次错误才翻转方向,因此对偶尔方向变化的分支具有"惯性",更能抵抗抖动。例如循环分支的最后一次误预测后,1-bit 会立即预测"不取"(下次循环开头又误预测一次),而 2-bit 需两次错误才翻转,循环重启时仍保持"取"预测,误预测更少。2-bit 是 1-bit 的增强,用少量状态换稳定性。

2-bit 相对 1-bit 增加了"惯性"(饱和计数器),对噪声/交替分支更稳定。这是预测器从简单到健壮的经典演进。

#
★★★

11. 解释 TAGE(TAgged GEometric history length)分支预测器中多个表的几何级数设计?

解释 TAGE(TAgged GEometric history length)分支预测器中多个表的几何级数设计?

  • TAGE 结构
  • 历史长度
  • 几何级数

TAGE(TAgged GEometric history length)用多个预测表(prediction tables),每个表对应不同的全局历史长度(global history length),且历史长度呈几何级数增长(如 0、2、4、8、16、32、64、128、...)。每个表项带 tag(索引+历史哈希),用 tag 匹配判断该表是否命中(能唯一关联该分支+历史)。预测时从最长历史长度的命中表中选择(若命中),用其计数器预测;若高历史表未命中则回退到较短历史表。几何级数分布在"短历史(处理频繁出现的近期模式)"与"长历史(处理复杂、长距离相关)"之间取得平衡,使表容量高效利用,准确率极高,是多数现代商用预测器(Intel、AMD、ARM)的基础。

TAGE 的几何级数历史长度用多个表覆盖从短到长的历史关联,配合 tag 匹配选择最精确的表,兼顾准确率与容量。这是当代分支预测器的黄金标准。

#
★★★

12. 解释 indirect branch 与 conditional branch 在预测器上的差异?

解释 indirect branch 与 conditional branch 在预测器上的差异?

  • 间接分支
  • 条件分支
  • 预测目标差异

conditional branch(条件分支)只需预测"取/不取"方向,目标通常是固定的(相对偏移),预测器只需 1 位方向,用 BHT/2-bit 计数器即可。indirect branch(间接分支,如 jmp [reg]、switch 多态调用)目标来自寄存器/内存,提前未知,预测器既要预测方向(通常总取)又要预测目标地址,需要 predictor 存储可能的目标地址(如 indirect branch predictor 用目标历史/路径哈希查表,预测具体目标)。C++ 虚函数调用、switch 语句是间接分支的典型,其目标多样,预测难度高,现代 CPU 用 indirect predictor(如 ITTAGE、BPU 的间接目标缓存)结合目标历史预测。

条件分支预测"方向",间接分支预测"目标地址"。间接分支目标多变,需要更复杂的预测机制(目标缓存+历史),是分支预测的难点。

#
★★★

13. 解释为何循环分支(loop branch)在多数 CPU 上几乎不误预测?

解释为何循环分支(loop branch)在多数 CPU 上几乎不误预测?

  • 循环分支行为
  • 预测器
  • 高准确率

循环分支(for/while 循环的退出判断)行为高度规律:绝大多数迭代"取"(继续循环),只有最后一次"不取"(退出)。因此循环分支高度偏向"取",2-bit 计数器/局部预测器几乎总是预测"取",只在最后一次迭代误预测一次。此外,现代 CPU 的预测器(如 TAGE、循环预测器 loop predictor)能识别循环的模式,甚至精确预测循环退出。因此 n 次循环的误预测率约 1/n(仅最后一次),n 大时几乎不误预测。这种"高偏向+规律"的特性使循环分支在多数 CPU 上表现极好。

循环分支"多数取、规律退出"的特征让预测器轻易达到高准确率。2-bit 计数器或专用循环预测器都能精准处理,故几乎不误预测。

#
★★★

14. 解释分支目标缓冲(BTB)与分支预测器(BHT)的区别?

解释分支目标缓冲(BTB)与分支预测器(BHT)的区别?

  • BTB 功能
  • BHT 功能
  • 方向 vs 目标

BTB(Branch Target Buffer)缓存分支指令的"目标地址"(以及预测的 next PC),用于预测分支跳转到哪里,从而无需等待计算目标即可取指。BHT(Branch History Table,或分支预测器)缓存分支的"方向历史/预测状态"(是否取),用 2-bit 计数器等预测分支是否跳转。区别:BTB 回答"跳到哪",BHT 回答"跳不跳"。两者常配合使用:BHT 预测方向,BTB 提供目标地址。现代 CPU 的取指单元用 BTB 获取目标 PC、用 BHT/预测器获取方向,共同构成分支预测的取指路径。

BTB 管"目标地址",BHT 管"方向(取/不取)"。二者是分支预测的两个侧面,BTB 提供跳转目标,BHT 决定是否跳转。

#
★★

15. 解释返回地址栈(RAS)如何预测函数返回地址?

解释返回地址栈(RAS)如何预测函数返回地址?

  • RAS 原理
  • 返回预测
  • 栈结构

RAS(Return Address Stack)是一个硬件栈,用于预测函数返回地址。当 CPU 遇到 call 指令时,把下一条指令地址(返回地址)压入 RAS;当遇到 ret 指令时,从 RAS 弹出栈顶作为预测的返回地址。由于函数调用/返回是严格的 LIFO 匹配,RAS 能精确预测返回地址(除非栈平衡被破坏,如异常、setjmp、尾调用优化)。RAS 比用 BTB 预测返回更准,因为返回目标由调用历史决定,BTB 无法区分多次调用同一函数的不同返回点。RAS 是返回预测的标准方案。

RAS 用与调用/返回严格匹配的 LIFO 栈预测返回地址,准确率高。返回地址由调用上下文决定,BTB 无法建模,RAS 因此不可或缺。

#
★★

16. 给定一个 32KB 4-way set-associative cache,64B line,画出地址 tag/index/offset 位拆分?

给定一个 32KB 4-way set-associative cache、64B line,画出地址的 tag/index/offset 位拆分?

  • 组相联缓存
  • 位拆分
  • index/offset/tag 计算

64B line ⇒ offset = 6 位(2^6=64)。总容量 32KB,每组 4 路 × 64B = 256B/组,组数 = 32KB / 256B = 128 组 ⇒ index = 7 位(2^7=128)。地址为 32 位(或 64 位,这里按 32 位算):低 6 位是 offset,接着 7 位是 index,剩余高位是 tag。32 位地址拆分:tag = 32 - 7 - 6 = 19 位,index = 7 位,offset = 6 位。即 [tag:19][index:7][offset:6]。

计算步骤:offset 由 line 大小决定(log2)、组数 = 容量/(路数×line),index 位数 = log2(组数),剩余为 tag。记住"容量 = 组数 × 路数 × line"。

#
★★

17. 给定一个 8-way 1MB L2 cache,画出 index/tag 位宽?

给定一个 8-way 1MB L2 cache,画出 index/tag 位宽?

  • 组相联缓存
  • index 位宽
  • tag 位宽

假设 line 为 64B(offset 6 位)。8-way 1MB:每组 8 路 × 64B = 512B/组,组数 = 1MB / 512B = 2048 组 ⇒ index = 11 位(2^11=2048)。地址 32 位:tag = 32 - 11 - 6 = 15 位。即 [tag:15][index:11][offset:6]。若地址 64 位,tag = 64 - 11 - 6 = 47 位。

组数 = 容量/(路数×line),index 位数 = log2(组数)。tag 位数 = 地址位数 - index - offset。题目未给 line 时按惯例 64B 计算。

#
★★

18. 解释 L1/L2/L3 多级缓存层次的设计动机与典型容量范围?

解释 L1/L2/L3 多级缓存层次的设计动机与典型容量范围?

  • 缓存层次
  • 容量/速度权衡
  • 局部性

缓存层次的设计动机是"在速度与容量之间折中":SRAM 越大越慢越贵,单级大缓存无法兼顾低延迟与高容量。多级缓存让最常用的数据留在最快的小缓存(L1),次常用数据进稍大的 L2,更大/更慢的 L3 覆盖更多工作集。典型容量:L1 通常 32-64KB(每核,延迟约 4-5 周期),L2 通常 256KB-1MB(每核,延迟约 10-20 周期),L3 通常 2-32MB(多核共享,延迟约 30-80 周期)。层次越大越慢但命中率越高,利用局部性原理(时间/空间局部性)让绝大多数访问命中 L1/L2,从而以接近 L1 的速度获得接近 L3 的容量。

缓存层次是"容量-延迟-成本"的三方权衡。局部性原理保证高层次命中率,使多级缓存总平均延迟接近最快层。这是存储层次设计的核心思想。

#
★★

19. 解释 cache line(缓存行)的概念与典型 64B 尺寸由来?

解释 cache line(缓存行)的概念与典型 64B 尺寸的由来?

  • cache line 概念
  • 块大小
  • 64B 由来

cache line(缓存行)是缓存与内存之间数据交换的最小单位,也是缓存中"一个条目"存储的数据块大小。访问一个字节会加载整个 cache line 到缓存。典型 64B 尺寸是权衡的结果:行越大,空间局部性利用越好(一次载入更多相邻数据)、tag 开销越小;但行过大,会浪费带宽(只用一个字节也要搬整行)、增加冲突与污染。64B 是 30 多年的经验平衡——现代 CPU(Intel/AMD/ARM)普遍采用 64B,兼顾空间局部性与带宽效率,也与 DRAM 突发传输、内存颗粒位宽匹配。

cache line 是缓存粒度单位。64B 是"局部性收益 vs 带宽浪费"的折中,成为工业标准。理解行大小对理解缓存命中/未命中、伪共享至关重要。

#
★★

20. 解释 victim cache 与 stream buffer 在 miss 优化中的角色?

解释 victim cache 与 stream buffer 在 miss 优化中的角色?

  • victim cache
  • stream buffer
  • miss 优化

victim cache(受害者缓存)是一个很小的全相联缓存,存放从主缓存中被替换出去的 cache line(victim)。当主缓存 miss 但该行最近刚被替换(还在 victim cache 中)时,可快速命中,缓解"冲突性 miss"(因替换策略导致的短暂 miss)。stream buffer(流缓冲)是预取缓冲区,检测顺序访问模式(如连续数组扫描),提前把后续的 cache line 预取到 stream buffer 中,掩盖 miss 延迟。两者都是 miss 优化手段:victim cache 缓解冲突 miss(减少替换抖动),stream buffer 缓解顺序访问的 miss(预取)。

victim cache 针对"被换出的行很快又被用到"的抖动,stream buffer 针对"顺序流的预取"。它们分别用"留住被替换行"和"提前预取"补足主缓存。

#
★★

21. 解释 write-back 与 write-through 两种写策略的差异?

解释 write-back 与 write-through 两种写策略的差异?

  • write-back
  • write-through
  • 写策略

write-through(写直达):写操作同时更新缓存和内存(或包含的下一级),优点是实现简单、内存与缓存始终一致,缺点是每次写都访问内存,写带宽消耗大。write-back(写回):写操作只更新缓存,把该行标记为 dirty,直到该行被替换时才一次性写回内存。优点是减少写内存次数、写带宽高效,缺点是需要维护 dirty 标记、替换时可能引入额外写延迟,且内存与缓存短期不一致。现代 CPU 的 L1/L2 多用 write-back(配合 write-allocate),write-through 用于强一致性要求或简单系统。

write-back 以"延迟写回"换取带宽,write-through 以"及时写"换取简单。现代 CPU 偏好 write-back 减少写流量,但需一致性协议配合。

#
★★

22. 解释为何 write-back 通常配合 write allocate 而 write-through 配合 no-write-allocate?

解释为何 write-back 通常配合 write-allocate 而 write-through 配合 no-write-allocate?

  • write-allocate
  • no-write-allocate
  • 写策略配对

write-allocate(写分配):写 miss 时先把块从内存加载到缓存再写入;no-write-allocate(写不分配):写 miss 时直接写内存/下一级,不加载到缓存。write-back 通常配 write-allocate:因为写回要等行最终写回内存,先把行载入缓存并在缓存中标记 dirty,后续可能多次写同一行,一次写回即可,避免每次写都访问内存。write-through 通常配 no-write-allocate:因为写直达每次写都更新内存,加载到缓存反而浪费带宽(写 miss 的块如果只写不读,缓存它无收益),直接写内存更高效。配对使写带宽最优。

write-allocate 与 write-back 互补(write-back 需要缓存行来承载 dirty 标记),no-write-allocate 与 write-through 互补(write-through 无需缓存行即可写内存)。这是写策略的经典配对。

#
★★

23. 解释 inclusive/exclusive cache 层次协议下 L3 与 L2 的关系?

解释 inclusive/exclusive cache 层次协议下 L3 与 L2 的关系?

  • inclusive cache
  • exclusive cache
  • L2/L3 关系

inclusive(包含)层次:L2 的内容是 L3 的子集,

L1 内容又是 L2 的子集,保证一致性时 L3 能作为 snoop 过滤点(L3 命中则无需查下级),简化一致性。exclusive(独占)层次:L2 与 L3 不重叠,数据只在一个层级,可用容量更大(避免重复存储),但 miss 查询需逐级查找。Intel 多用 inclusive(L3 包含 L2),AMD 的某些设计用 exclusive 或非包含(non-inclusive)。inclusive 简化一致性但浪费容量(重复存储),exclusive 提高有效容量但增加一致性复杂度。L3 与 L2 的关系决定了缓存利用率与一致性机制。

inclusive/exclusive 是缓存层次的组织方式。inclusive 用容量换一致性简化,exclusive 用一致性复杂度换容量。这是 L2/L3 关系设计的核心取舍。

#
★★

24. 解释 GHB(Global History Buffer)预取器的工作机制?

解释 GHB(Global History Buffer)预取器的工作机制?

  • GHB 预取器
  • 全局历史
  • 预取机制

GHB(Global History Buffer)是一种基于全局历史相关性的预取器。它记录访存指令的全局历史:哪条 PC 访存、访问了哪些地址、观察到的地址增量(delta)。GHB 通过记录"访存 PC 与地址增量"的关联,检测跨指令的访存模式(如数组的多个元素被不同指令访问)。预取时,若当前访存 PC 的历史显示后续有规律访问,GHB 就用历史中的规律预测并预取后续地址。它把"空间关联"(delta 模式)融入全局历史,能发现比单条指令 strided 更复杂(跨指令)的访存模式,是高级预取器之一。

GHB 用"全局访存历史 + 地址增量"检测跨指令的访存模式,比简单 stride 预取器更智能。它是学术与工业预取研究的重要方向。

#
★★

25. 解释为何 ARM 在 prefetcher 上增加 AMP(Adaptive Multipath)?

解释为何 ARM 在 prefetcher 上增加 AMP(Adaptive Multipath)?

  • AMP 预取
  • 自适应多路径
  • 预取准确性

传统 stride/stream 预取器只跟踪单一访存路径,遇到多数据流(多个数组、多个对象)时易混淆或预取不足。ARM 的 AMP(Adaptive Multipath)预取器跟踪多个并行的访存路径(multipath),为每个路径独立维护预测,并根据历史准确率自适应选择/调整活跃路径,同时引入预取预算控制(防止过度预取浪费带宽)。通过多路径跟踪,AMP 能同时预取多个数据流,提高覆盖率;通过自适应选择,减少无效预取(污染),提升预取准确率与带宽效率。这是 ARM 对复杂多流工作负载的预取优化。

AMP 用"多路径跟踪 + 自适应调度"提升预取在多变数据流场景下的准确率与覆盖率,同时控制带宽。这是预取器从单路径向多路径演进的体现。

#
★★

26. 解释 memory dependence predictor(MDP)与 store-set 预测器?

解释 memory dependence predictor(MDP)与 store-set 预测器?

  • 内存依赖预测
  • store-set
  • 投机 load

乱序执行中,load 是否依赖前面的 store(内存地址相同)无法仅靠寄存器确定,若不预测则会过度保守(阻塞 load 直到所有前序 store 就绪),或过度投机(load 提前执行但可能读到旧值)。memory dependence predictor(MDP)预测哪些 load 可能与哪些 store 冲突:store-set 预测器维护一个"store 集合"(store-set),把每个 load 关联到它历史上冲突过的 store 集合,预测时若 load 的 store-set 非空则让其等待对应 store,否则可提前执行(投机)。这样在保证正确性的前提下,让无冲突的 load 提前执行,提升内存并行度。预测错误时用回滚/重放纠正。

store-set 预测器把"load 会冲突的 store 集合"记录下来,据此决定 load 是否可提前执行,是投机 load 与内存依赖预测的关键。它平衡了"安全"与"并行"。

#
★★

27. 解释 2-bit counter 在 strongly biased 分支上为何仍能稳定?

解释 2-bit counter 在 strongly biased 分支上为何仍能稳定?

  • strongly biased 分支
  • 2-bit 计数器
  • 稳定性

strongly biased 分支是指几乎总是取(或几乎总不取)的分支,如循环条件、异常检查。2-bit 饱和计数器在"强取"(11)状态:即使偶尔一次方向错误(取错),计数器也只从 11 降到 10(弱取),仍预测"取",不会立即翻转。这种"惯性"(需要连续两次错误才翻转方向)使强偏向分支在少量噪声下依然保持稳定预测。所以 2-bit 计数器对强偏向分支几乎永远正确,只有连续两次以上错误才会改变方向,从而保持高准确率。

2-bit 饱和计数器的"饱和+惯性"特性使其对强偏向分支稳定:单次错误不改变预测方向,需连续两次错误才翻转。这是其抗噪能力所在。

#
★★

28. 解释 indirect branch predictor(如 IBT)为何采用路径哈希?

解释 indirect branch predictor(如 IBT)为何采用路径哈希?

  • 间接分支预测
  • 路径哈希
  • 目标区分

间接分支(如虚函数调用、switch)的目标取决于程序执行路径(调用上下文),同一分支指令在不同路径下可能跳转到不同目标。若只用 PC 索引,无法区分不同路径,预测会冲突。间接分支预测器(如 IBT、ITTAGE)用"路径哈希"(path hash):把最近执行的指令 PC 序列哈希成索引,与当前分支 PC 结合,从而让不同路径映射到不同表项,区分不同调用上下文下的目标。路径哈希捕捉了"到达该分支的路径",提高了目标预测的区分度与准确率。现代预测器用全局历史+PC 哈希(如 TAGE 的 tag 机制)实现类似效果。

路径哈希让预测器能区分"不同路径到达同一分支"的不同目标,是间接分支预测准确率的关键。它把路径历史编码进索引。

#
★★

29. 解释为何 L1 命中延迟(L1 hit latency)在 Intel/AMD 通常为 4-5 cycle?

解释为何 L1 命中延迟(L1 hit latency)在 Intel/AMD 通常为 4-5 cycle?

  • L1 命中延迟
  • 流水线阶段
  • 周期限制

L1 命中延迟 4-5 周期由 L1 缓存的物理实现决定:访问 L1 需要经历地址译码(index 位)、tag 比较(与虚拟/物理地址比对)、数据读取(从 SRAM 阵列读出)等阶段,这些阶段无法在单个周期内完成,需要拆成多个流水级。在给定的工艺/主频下,L1 的 SRAM 规模(约 32-64KB)与比较逻辑决定了关键路径,4-5 周期是"SRAM 访问延迟 ≥ 数个周期"的工程折中。用更深的流水(更多周期)可将 L1 做更大,但增加 load-use 延迟;4-5 周期是容量与延迟的平衡点。相比 1-2 周期的寄存器和数十周期的 L3,L1 的 4-5 周期是速度与容量权衡的结果。

L1 延迟 4-5 周期是"SRAM 物理访问 + 比较逻辑"在目标主频下的关键路径,也反映容量与延迟的权衡。它与 L2/L3 的数十周期形成缓存层次。

#
★★

30. 解释 Intel Pentium 4(RWT)与 AMD Zen 的 ROB 容量差异?

解释 Intel Pentium 4(RWT)与 AMD Zen 的 ROB 容量差异?

  • Pentium 4 ROB
  • AMD Zen ROB
  • 微架构差异

Pentium 4(RWT,NetBurst 架构)的 ROB 容量约 126 项,与其"深流水+高主频"策略匹配,但也因深流水和较小 ROB 导致误预测惩罚大、功耗高。AMD Zen(Zen 3)的 ROB 容量约 224 项(Zen 3 为 224,Zen 2 为 192),更大,能容纳更多在飞行指令,乱序窗口更宽,ILP 更高,能更好地掩盖缓存缺失等长延迟。差异体现了两代设计哲学:Pentium 4 追求极端主频(深流水、浅 ROB),Zen 追求更宽的乱序执行(大 ROB)以提升 IPC。更大的 ROB 让 Zen 在相同主频下能调度更多指令。

ROB 容量决定乱序窗口深度。Pentium 4 深流水高主频但 ROB 小,Zen 大 ROB 提升 IPC,反映"高主频 vs 高 IPC"两种设计路线。

#
★★

31. 解释为何 speculative load 需要在分支确认前阻塞 store?

解释为何 speculative load(投机 load)需要在分支确认前阻塞 store?

  • 投机 load
  • store-blocking
  • 内存排序

speculative load 是分支预测正确前就执行的 load。若它提前以乱序方式执行,可能读到"未确认分支路径上"的 store 尚未写入的值,或读到后续 store 之前的旧值,破坏内存一致性。因此 load 在访问内存前,需要确认与其地址可能冲突的、处于同一分支路径上的更早 store 是否已就绪;若未就绪,load 必须阻塞等待(store-blocking),直到确认没有冲突的 store 或该 store 已提交。这保证了 load 读到的值符合程序顺序语义。若预测分支错误,这些投机 load 被丢弃重做。所以投机 load 需在分支确认前阻塞潜在冲突的 store,以保证正确性。

投机 load 提升性能,但必须保证内存顺序正确。store-set 预测器决定是否阻塞:冲突的 store 未就绪则阻塞 load,避免读到错误值。这是正确性与性能的平衡。

#
★★

32. 解释 load-use 冒险在 OoO 处理器中如何被动态调度隐藏?

解释 load-use 冒险在 OoO 处理器中如何被动态调度隐藏?

  • load-use 冒险
  • OoO 调度
  • 隐藏延迟

在顺序流水线中,load-use 冒险(load 结果被后续立即使用)需要 stall。在 OoO 处理器中,load 发射后结果就绪需要若干周期(L1 命中 4-5 周期),但 OoO 调度器可以在 load 等待期间,继续执行其他就绪的、无依赖的指令(不完全按程序顺序),从而用"其他指令"填满 load 延迟,隐藏 load-use 的等待。只要 ROB/保留站中有足够的无依赖指令,load-use 的延迟就被掩盖。若指令依赖 load 结果且无其他可执行指令,则仍会 stall,但 OoO 最大化利用 ILP 将这种等待降到最低。

OoO 用"乱序调度 + 指令并行"隐藏 load 延迟:load 等待时执行其他指令。这是 OoO 相比顺序流水线性能提升的关键,但依赖 ILP 与流水线资源。

#
★★

33. 解释为何 gshare = (PC XOR GHR) 能降低 aliasing?

解释为何 gshare = (PC XOR GHR) 能降低 aliasing?

  • gshare 预测器
  • PC XOR GHR
  • aliasing 降低

gshare 预测器用"PC XOR 全局历史寄存器(GHR)"作为索引。若只用 PC 索引,不同分支映射到同一表项会产生 aliasing(冲突),尤其是"同一 PC 在不同历史状态下"的模式被折叠。gshare 把全局历史(GHR)异或进 PC:GHR 记录了最近的分支行为,与 PC 结合后,同一 PC 在不同历史下对应不同索引,从而区分不同分支/不同上下文,减少冲突锁(aliasing)。即使两个分支 PC 相同,只要历史不同,索引大概率不同。XOR 是一种低成本、良好分布的方式,把历史信息混合进索引,降低冲突,提高预测准确率。

gshare 的精华是"用全局历史丰富索引",PC XOR GHR 让索引同时反映"哪个分支"与"什么历史",降低 aliasing。这是经典 gshare 预测器胜过纯 PC 索引的原因。

#
★★

34. 解释全局历史(GHR)、局部历史(PHT)两种预测器输入差异?

解释全局历史(GHR)、局部历史(PHT)两种预测器输入的差异?

  • 全局历史
  • 局部历史
  • 预测器输入

全局历史预测器(如 gshare、gselect)用全局历史寄存器(GHR,记录所有最近分支的方向序列)作为索引,捕捉"分支间的相关性"(不同分支的相互影响),适合分支间存在相关的工作负载。局部历史预测器用每分支自己的历史(如 PHT,Per-branch History Table,记录该分支最近几次方向)作为索引,捕捉"单个分支自身的模式",适合分支自身行为规律但与其他分支无关的负载。差异:全局历史看"跨分支"的相关性,局部历史看"单分支"的历史。现代预测器(如 TAGE)结合两者,用全局历史为主、局部历史为辅。

GHR 反映全局上下文,PHT 反映单分支自我模式。二者捕捉不同相关性,现代预测器融合两者以提升准确率。

#
★★

35. 解释为何 SPEC int 2017 中 mcf 误预测率显著高于 bzip2?

解释为何 SPEC int 2017 中 mcf 误预测率显著高于 bzip2?

  • mcf 分支模式
  • bzip2 分支模式
  • 误预测差异

mcf(最小代价流)的分支主要来自树/图的遍历和指针相关比较,分支目标高度依赖数据内容(如比较节点代价、路径选择),数据分布近似随机,分支行为难以预测,因此误预测率显著高。bzip2(压缩)的分支主要来自可预测的循环和模式匹配,分支行为相对规律(如位流处理、循环边界),预测器能较好捕捉,误预测率低。本质差异:mcf 的分支是"数据相关的、难以静态/动态预测"的,bzip2 的分支是"结构规律、可预测"的。这体现了预测器对"数据相关分支"的局限性。

误预测率差异源于分支可预测性。mcf 数据相关分支(图/树遍历)难预测,bzip2 规律循环好预测。这是 SPEC 基准中分支行为差异的经典案例。

#
★★

36. 解释 LRU(最近最少使用)与 PLRU(伪 LRU)替换策略的硬件开销差异?

解释 LRU(最近最少使用)与 PLRU(伪 LRU)替换策略的硬件开销差异?

  • LRU
  • PLRU
  • 硬件开销

LRU(最近最少使用)需要为每个缓存组记录精确的访问顺序(如 4 路需 4 种状态、n 路需 log2(n!) 位),每次访问都要更新顺序,硬件实现复杂、占用面积大、访问延迟高。PLRU(伪 LRU,如 tree-based PLRU)用"树状位"近似较久未用的行,只需 n-1 位(对 n 路),每次访问更新少量位,硬件简单、速度快、面积小。PLRU 以轻微的替换精度损失(可能替换非最久未用的行)换取显著降低的硬件开销,因此被 Intel/AMD 等广泛采用(如 Intel 4 路 L1 用 PLRU)。差异:LRU 精确但贵,PLRU 近似但省硬件。

LRU 精确需要大量状态位,PLRU 用树状位近似以省硬件。PLRU 在精度与成本间折中,是工业缓存的常用选择。

#
★★

37. 解释直接映射(direct-mapped)与组相联(N-way set-associative)的冲突差异?

解释直接映射(direct-mapped)与组相联(N-way set-associative)的冲突差异?

  • 直接映射
  • 组相联
  • 冲突 miss

直接映射(direct-mapped)中,每个地址只能映射到唯一的缓存组(组内只有 1 路),因此地址映射到同一组的多个数据会互相冲突,无法同时驻留,导致冲突 miss(conflict miss)。组相联(N-way set-associative)中,每个地址映射到一组,组内有 N 个"路"可放,多个刚冲突的地址可同时放在不同路,显著减少冲突 miss。路数越多,冲突 miss 越少,但 tag 比较/选择逻辑更复杂、延迟更高。直接映射硬件最简单、延迟最低但冲突多;组相联用硬件复杂度换命中率。

相联度决定"同一组能容纳多少候选",直接映射冲突严重,组相联缓解冲突。这是缓存设计命中率与复杂度的核心权衡。

#

38. 解释 prefetch throttling 在 LLC miss rate 上升时的策略?

解释 prefetch throttling 在 LLC miss rate 上升时的策略?

  • prefetch throttling
  • LLC miss rate
  • 带宽控制

prefetch throttling(预取节流)是调整预取强度/数量的机制。当 LLC(末级缓存)miss rate 上升时,说明预取可能过度(污染缓存、浪费带宽)或系统带宽紧张,硬件会降低预取强度(减少预取距离、降低预取数量、暂停部分预取流),以保护有用的缓存容量与内存带宽。反之,当 miss rate 降低且带宽充足时,可提高预取强度。节流的目标是在"预取收益"与"预取造成的缓存污染/带宽消耗"之间动态平衡,避免预取过度导致性能下降。现代 CPU 根据 miss rate、带宽利用率等指标实时调节预取。

prefetch throttling 是"自适应的预取控制",防止预取过度。LLC miss rate 上升时减少预取,保护带宽与缓存有效容量。这是现代预取器的关键机制。

#

39. 解释 prefetcher 对访存模式的探测,strided / stream / pointer-chasing 如何识别?

解释 prefetcher 对访存模式的探测:strided / stream / pointer-chasing?

  • strided 探测
  • stream 探测
  • pointer-chasing 探测

prefetcher 探测不同访存模式:strided(跨步)探测固定步长访问(如数组元素间隔固定),记录上次地址与步长,预测下一个地址并预取;stream(流式)探测连续顺序访问(如数组线性扫描),检测到顺序流后按固定距离预取后续 cache line;pointer-chasing(指针追逐)探测链式/间接访问(如链表、树节点通过指针链接),此时地址是数据相关的,无法用简单步长预测,需特殊机制(如多层预取、依赖预取)或难以预取。三者对应不同访存规律:strided 用步长、stream 用顺序、pointer-chasing 用依赖。

预取器先识别访存模式再定预取策略。strided 和 stream 是"规律地址"可预取,pointer-chasing 是"数据依赖地址"难预取,这是预取收益差异的根源。

#

40. 解释 software prefetch 指令(prfm/prefetcht0)与 hardware prefetch 的互补?

解释 software prefetch 指令(prfm/prefetcht0)与 hardware prefetch 的互补?

  • software prefetch
  • hardware prefetch
  • 互补

software prefetch(软件预取)由程序员/编译器显式插入预取指令(如 ARM 的 prfm、x86 的 prefetcht0/prefetchnta),把数据提前搬到缓存,可控性强、能针对程序特有的访存模式(如不规则数据、指针追逐)。hardware prefetch(硬件预取)由 CPU 自动检测规律模式(顺序、跨步)自动预取,无需程序干预,但只对硬件能识别的规律模式有效。两者互补:硬件预取覆盖常见规律访问(零成本、自动),软件预取覆盖硬件无法识别的复杂/不规则模式(需要代码介入)。程序员可先用硬件预取,对热点不规则访问用手写 prefetch 指令优化。

hardware prefetch 自动但局限,software prefetch 可控但需代码。二者互补构成完整预取策略:硬件兜底规律访问,软件补充不规则模式。

#

41. 解释 stream prefetcher 在多大 stride 范围内有效?

解释 stream prefetcher 在多大 stride 范围内有效?

  • stream prefetcher
  • stride 范围
  • 有效性

stream prefetcher(流式预取器)检测顺序/小跨步访问模式并预取。它的有效 range 通常针对"小且固定的 stride":经典的 stream prefetcher(如 Intel 的 adjacent-line prefetcher)对 stride = 1 的连续访问(相邻 cache line)最有效,推出 2 个相邻行;对 stride 较小(如 2、4)且固定也能捕捉。但 stride 过大(如间隔 64B 或更大、非规则)时,stream prefetcher 失效,需要 stride prefetcher(记录具体步长)或多步预取。因此 stream prefetcher 主要对"相邻/小步长顺序流"有效,复杂跨步交给专门的 stride 预取器。

stream prefetcher 面向相邻/小跨度顺序流,stride 大或可变时失效。覆盖范围是"顺序流",跨步流由 stride prefetcher 负责。

#

42. 解释为何数据库顺序扫描受益于 hardware prefetcher?

解释为何数据库顺序扫描受益于 hardware prefetcher?

  • 顺序扫描
  • 空间局部性
  • 预取收益

数据库顺序扫描(如全表扫描、按行扫描)按顺序访问内存中的连续数据,是高度规律、空间局部性极强的访问模式。hardware prefetcher 能检测这种顺序流,提前把后续 cache line 预取到 L1/L2,把 miss 延迟隐藏(在数据被使用时已就绪)。由于顺序扫描的地址完全可预测,预取准确率极高、几乎无浪费,因此 memory 带宽被充分利用,miss 不再阻塞。结果:顺序扫描的停滞时间大幅减少,吞吐接近内存带宽上限。这是硬件预取器对"顺序大流"访问的典型收益场景。

顺序扫描是"完美可预测 + 高空间局部性"的访问,预取器能完美覆盖,把 miss 延迟转为后台带宽利用,故收益巨大。数据库全表扫描正是受益者。

#

43. 解释为何链式数据结构(如链表)的遍历 prefetcher 收益有限?

解释为何链式数据结构(如链表)的遍历 prefetcher 收益有限?

  • 链式遍历
  • 指针追逐
  • 预取局限

链表遍历是典型的 pointer-chasing(指针追逐):访问下一个节点需要先读出当前节点的指针(next),地址是数据依赖的,无法预先用步长/顺序预测。prefetcher 必须在读到当前节点的 next 指针后才能知道下一个地址,等于是"内存访问依赖内存访问",把预取延迟累加。即使硬件能推测 next 指针(依赖预取/多步预取),也因指针字段可能不在 cache line 首部、且需要额外访存而收益有限。因此链式遍历的预取收益远低于顺序数组,因为地址依赖链使预取无法提前并行。解法是把链表改成数组/SoA(提供空间局部性)。

链式访问的地址依赖使预取无法提前,是"延迟依赖"的本质。预取器对顺序流有效、对指针追逐无效,这是结构性差异。

#

44. 解释时间局部性(temporal locality)与空间局部性(spatial locality)的差异?

解释时间局部性(temporal locality)与空间局部性(spatial locality)的差异?

  • 时间局部性
  • 空间局部性
  • 局部性原理

时间局部性(temporal locality):被访问的数据在不久的将来很可能再次被访问(如循环变量、计数器、循环体中的变量),缓存利用这一点保存近期访问的数据。空间局部性(spatial locality):被访问的数据附近的数据很可能很快被访问(如数组连续元素、指令顺序),缓存利用这一点按 cache line 加载相邻数据。差异:时间局部性关注"同一地址的重复访问",空间局部性关注"相邻地址的访问"。两者共同支撑缓存设计:时间局部性靠"保留热数据",空间局部性靠"按行加载+预取"。

时间局部性 = 数据重复访问,空间局部性 = 相邻访问。缓存与预取分别利用两者,是缓存层次与预取器设计的理论基础。

#

45. 解释顺序预取(sequential prefetch)与跨步预取(stride prefetcher)的适用场景?

解释顺序预取(sequential prefetch)与跨步预取(stride prefetcher)的适用场景?

  • sequential prefetch
  • stride prefetcher
  • 适用场景

顺序预取(sequential prefetch)适用于纯顺序访问(stride=1,如数组线性扫描、图像逐行处理),预取下一个/相邻 cache line,硬件简单、覆盖率高。跨步预取(stride prefetcher)适用于固定步长访问(stride 为 2、4、多元素跨度,如矩阵按列访问、每 N 个元素取一个),记录步长并预测下一个地址,覆盖非连续但有规律的模式。适用场景差异:顺序预取处理"连续流",跨步预取处理"间隔固定但跨步"的访问。步长较大或不规律时,跨步预取也会失效,需更复杂预测。

sequential prefetch 面向 stride=1 的连续流,stride prefetcher 面向固定跨步流。选择取决于访存步长是否固定、是否连续。

#

46. 解释 prefetch distance(提前多少 cycle 预取)的经验值?

解释 prefetch distance(提前多少 cycle 预取)的经验值?

  • prefetch distance
  • 提前量
  • 经验值

prefetch distance 是预取操作比实际使用提前的周期数/距离,需与内存延迟匹配。若距离太小,预取的数据未及时到达,无法掩盖 miss 延迟;若距离太大,预取的数据可能被替换/污染,且过早占用缓存。经验值是让预取距离 ≈ 内存延迟 / 每一行消耗的周期,典型取"覆盖内存延迟所需的若干行/周期"。对顺序流,预取距离通常设在能提前 1-2 个 cache line 到几十个周期,使预取在数据使用前若干周期完成。硬件预取器动态调整距离,软件预取(如 prefetcht0)常提前数次迭代(如提前 8-16 次迭代)。

prefetch distance 需匹配内存延迟与访问速率,过小掩盖不掉 stall,过大浪费/污染。经验上取"覆盖延迟的提前量",硬件自适应、软件靠经验值。