SICI · 天机门 · 第十二章

流水贯脉

指令级并行 · 结丹一层

你以为5级流水线就是5倍加速。但结构、数据、控制三类冒险让流水线不断停顿——加速比永远摸不到那个极限。气泡不是bug,是冒险的代价。

门楣刻字:「5级,5倍。你说是不是?」
序章

贯脉之门

5级流水线,几倍加速?

结丹试炼的甬道尽头,是一扇刻满电路纹路的石门。门楣上「贯脉」二字被一层半透明的气泡遮住了一半。

结丹试炼甬道尽头刻满电路纹路的石门,门楣「贯脉」二字被半透明气泡遮住一半

石台上摆着一块晶板,上面刻着一条指令的执行轨迹:取指、译码、执行、访存、写回——五步,五拍,走完一条,才轮到下一条。

诺依曼老祖 诺依曼老祖

「你学了数据通路,知道一条指令怎么走。但每次只走一条——你不觉得浪费吗?」

气泡之魔 气泡之魔

「5级,5倍。你说是不是?」

流水线加速预测挑战器

先下注,再拖指令数对比串行/流水线两种模式

气泡之魔逼你下注:流水线5级,加速几倍?
详解 · K12-01 流水线的基本概念:类比与性能分析

Patterson & Hennessy《计算机组成与设计》Ch4 的经典类比:流水线不是让一件衣服洗得更快,而是让多桶衣服的洗、烘、叠三道工序重叠——单件延迟一点没变,吞吐量却翻了近三倍。S0 台上那个赌局正是这道理的数字版:5 级流水线、单条指令仍需 5 拍,但 10 条指令从串行 50 拍压到 14 拍(n+k-1),S=3.57。三个常被混淆的量必须分清:延迟(一条指令走完的时间,流水线不降反可能升——级间寄存器要付 setup/hold 开销)、吞吐量(稳态下每拍完成一条)、加速比(S=kn/(n+k-1),上限是 k)。还有一条常被忽略的硬约束:时钟周期由最慢一级决定,若五级延迟是 1:1:1:1:4,切得再细也只有 Σtᵢ/max(tᵢ)=2 倍的份——不平衡的流水线,加速比被最慢级卡死。「5倍」赌注输在两处:充满开销(k-1 拍)与级不平衡,前者随 n 增大稀释,后者与 n 无关、靠重新划分流水级才能救。

详解 · K12-15 流水线效率分析:流水线充满/排空开销

效率 η 的定义是流水线硬件的平均时间利用率:η = S/k = n/(n+k-1)。S0 滑块拉一遍就能看到 η 的台阶:n=10 → 71.4%,n=100 → 96.2%,n=1000 → 99.6%——分子分母里那个 k-1 就是充满(fill)与排空(drain)的 4 拍,n 越大摊得越薄,但永远不为零,这正是「加速比趋近 k 却永不到 k」的代数根源。Hennessy & Patterson《计算机体系结构:量化研究方法》Ch3 把这条账并入流水线 CPI 公式:CPI_实际 = CPI_理想 + (k-1)/n。工程上对付充满开销的思路是「让 n 变大」:循环展开(loop unrolling)把 4 个短迭代并成 1 个长迭代,开销摊薄 4 倍;软件流水(software pipelining)更进一步,把不同迭代的指令重新排布,让充满期也在做有用功。口径提醒:η 是时间利用率不是能效——排空期那些时钟翻转的能量照付,这正是低功耗设计谈「时钟门控」的动机之一。

第一幕 · 自信

五级法阵 · 流水贯脉

把五块功能石放进去,法阵就活了

石门内是一座五层环形法阵,五层之间隔着透明的级间屏障——那是级间寄存器。

诺依曼老祖 诺依曼老祖

「五级,五脉。取指(IF)、译码(ID)、执行(EX)、访存(MEM)、写回(WB)。指令从顶层流入,逐级下沉,每一拍前进一步。把五块功能石放进去。放对了,法阵就活了。」

五级法阵流水线动画器

拖功能石归位 → 逐拍推进时空图 → 看法阵充满

流水线时空图

详解 · K12-02 经典五级流水线:IF/ID/EX/MEM/WB

Patterson & Hennessy《计算机组成与设计》Ch4 的 MIPS 教学模型把一条指令的生命周期切成五段:IF 从 I-Cache 取指并递增 PC;ID 译码、读寄存器堆、符号扩展立即数;EX 做 ALU 运算与分支比较;MEM 读写 D-Cache;WB 把结果写回寄存器堆。切分不是拍脑袋,而是三条硬约束:各级延迟要均衡(时钟周期被最慢一级钉死)、级边界切在数据的自然断点上、四组级间寄存器(IF/ID、ID/EX、EX/MEM、MEM/WB)必须携带下一级需要的全部状态——S1 法阵五层之间的透明屏障就是它们,每拍全体同步前移一拍。两个常被忽略的设计巧合:寄存器堆「前半拍写、后半拍读」让 WB 与 ID 共用端口不冲突;I-Cache 与 D-Cache 分体让 IF 与 MEM 各走各的口——这条若不满足,就是 S3 结构冒险的现场。S1 单拍推进后时空图走出的对角线,就是指令 i 在第 i+s−1 拍流过第 s 级的解析式:10 条指令 14 拍收工(n+k−1)。五级是 1980 年代的工程平衡点,现代 CPU 早挖到 14~20 级(代价见 S10),但五级模型至今仍是理解一切指令级并行的地基——先会按序重叠,才谈乱序发射。

第一幕 · 自信

吞吐天平 · 理论之巅

拖因子组公式,称出流水线有多快

法阵旁悬着一台天平,左盘刻「吞吐量」,右盘刻「加速比」,中间立着一根CPI标尺。

诺依曼老祖 诺依曼老祖

「法阵搭好了。现在称一称——它到底有多快。别猜,算。」

气泡之魔 气泡之魔

「5倍嘛。你S0不就猜过了?这回有公式了,更稳了。」

吞吐天平公式组装器

拖因子组装5个公式 → n滑块验证 → 计算练习

详解 · K12-03 流水线的性能指标:吞吐量、加速比、CPI

S2 天平上组装的五个公式是流水线性能的全部骨架:吞吐率 TP = n/(n+k−1) 条/拍(稳态趋近 1);加速比 S = kn/(n+k−1)(上限是 k);实际 CPI = (n+k−1)/n = 1 + (k−1)/n;效率 η = S/k = n/(n+k−1);时钟周期 T_clk = max(t_stage) + t_寄存器开销。注意 S 与 CPI 的换算关系 S = k/CPI(k=5 时 S=5/CPI)——这是 S5~S8 一路把 CPI 从 1.60 压回 1.015 的理论依据。代入验算 S2 滑块:n=100 → S=500/104≈4.81、CPI=1.04、η=96.2%;n=1000 → S≈4.98、CPI=1.004——k−1=4 拍的充满开销被 n 稀释,但分母里永远挂着。Hennessy & Patterson《计算机体系结构:量化研究方法》Ch3 把账拆得更细:CPI_实际 = CPI_理想 + 结构停顿 + 数据停顿 + 控制停顿,后三项正是 S3/S5/S7 三幕的账本,也是 S9 总清算的科目。工程口径提醒:比较两款流水线不能只看 S——若 5 级机时钟 1ns、10 级机时钟 0.6ns,同是「理论 5 倍」含金量完全不同;性能的最终裁判只有一个:执行时间 = 指令数 × CPI × T_clk。

第一幕 · 崩塌

结构裂隙 · 第一崩塌

理论4.98倍,实际能跑出多少?

你把指令序列灌入法阵,正准备欣赏5倍加速的盛景——第3拍,法阵发出一声刺耳的摩擦声。IF层的取指光球和MEM层的访存光球同时撞向同一块存储晶石。

诺依曼老祖 诺依曼老祖

「理论是天平上的数字。现实是——你的存储器只有一个端口。」

结构冒险触发器 · 单端口存储器冲突

运行触发IF/MEM冲突 → 插入气泡 → 拆分I-Cache/D-Cache

单端口存储器
详解 · K12-04 结构冒险(Structural Hazard):资源冲突与解决方案

结构冒险的定义:同一拍,两条指令请求同一硬件资源。五级流水线最典型的就是 S3 演的这出——指令 i 的 MEM 要访问存储器取数据,指令 i+3 的 IF 也要访问存储器取指令,而单端口存储器一拍只服务一个,后到者必须停一拍、插 NOP。解法按花钱多少排三层:资源复制(I-Cache 与 D-Cache 分体,即哈佛结构,代价是多一份阵列的硅面积与功耗——S3 拆分按钮按下去付的就是这笔钱)、资源流水化(把访问本身切成多级,让多个请求错拍共享)、停顿(认赔,性能直接扣)。Patterson Ch4 的量级感:若每 5 条指令撞一次、每次停 1 拍,CPI 就恶化 0.2——灾难级;拆 I/D 后此账清零,所以现代商用 CPU 无一例外分体。值得记住的定位:三类冒险里,结构冒险是唯一能靠花钱(面积)一次性买断的——数据冒险与控制冒险花钱也买不断,只能权衡,这正是 S3 只配当「第一崩塌」、真正的深渊在 S5/S7 的原因。残留战场:多功能部件共享(唯一的除法器、唯一的 FMA 端口)在乱序机里仍会结构冒险,调度器必须排队——买断要做彻底,半买断等于没买。

第二幕 · 挣扎

数据暗流 · 依赖之网

RAW / WAR / WAW——哪种才是真依赖?

结构裂隙修好了,法阵恢复流畅。你正松一口气——时空图上,两条指令之间忽然亮起一根红线。ADD R1,R2,R3 后面跟着 SUB R4,R1,R5。它读的R1,还没写进去。

诺依曼老祖 诺依曼老祖

「这不是资源冲突。这是逻辑依赖——后面的指令,需要前面指令的结果。」

数据依赖识别器 · RAW/WAR/WAW

为三组指令序列标注依赖类型 → 分析五级流水线影响

详解 · K12-05 数据冒险(Data Hazard):RAW/WAR/WAW依赖

三类依赖由寄存器的读写次序定义:RAW(Read After Write,真依赖——后读者要前写者的新值)、WAR(Write After Read,反依赖——后写者不能毁掉前读者还没读走的旧值)、WAW(Write After Write,输出依赖——两次写不能换序)。S4 依赖之网的关键洞见:只有 RAW 是程序语义的一部分——ADD R1,R2,R3 之后的 SUB R4,R1,R5 真的必须拿到 R1 的新值,换任何寄存器名字都消不掉;WAR/WAW 是「假依赖」,纯粹因为逻辑寄存器名字不够用而起,寄存器重命名(register renaming)换一串物理名即可根除——这正是 ch13 乱序执行的看家本领(Tomasulo 保留站与 ROB 都建立在这上面)。五级机里的攻防格局:读在 ID(第 2 级)、写在 WB(第 5 级),读写天然拉开距离,加上寄存器堆前半拍写后半拍读,WAR/WAW 在按序五级机里根本构不成威胁——S4 标注三组序列验算可得:真依赖只咬距离 ≤2 的指令(相邻停 2 拍、隔一条停 1 拍),距离 ≥3 时 WB 与 ID 恰好错开半拍、自然化解。所以数据冒险战场的主角自始至终只有一个:RAW,且只有近身才有杀伤力——S5 的停顿与 S6 的前递,全部围绕它展开。

第二幕 · 再崩塌

气泡深渊 · 第二崩塌

停一拍就好了?看看CPI怎么崩

RAW依赖的红线悬在时空图上。气泡之魔从裂缝中钻出来,盘在法阵上方,声音甜得发腻:

气泡之魔 气泡之魔

「停一拍就好了。等它写完再读——安全,稳妥,天经地义。」

诺依曼老祖 诺依曼老祖

「你亲手塞进去的,它记在你的法阵上。」

气泡深渊三旋钮实验台

调冒险密度 / 前递开关 / Load-use频率 → 看CPI与加速比曲线

数据冒险密度
0%
前递开关
关闭
Load-use频率
0%

本命流水线 · 气泡疤痕

CPI = 1.004 加速比 S = 4.98 气泡数 = 0 前递省停顿 = 0
详解 · K12-07 流水线停顿(Stall)与插入气泡(Bubble)

停顿的机制动作分两半:冒险检测发现近身 RAW 且无前递时,冻结 PC 与 IF/ID 级间寄存器(前端原地踏步,同一条指令不许再前进),同时向 ID/EX 注入一条全零的 NOP——气泡(bubble)。NOP 不读不写不跳,只占流水线的位置,把后继指令整体推迟。S5 三旋钮实验台的可验算账本:CPI = 1 + 密度×2×(1−前递覆盖率) + Load-use频率×1。前递关死、密度拉到 30% 时 CPI=1+0.6=1.60、S=5/1.6≈3.13——「5倍」直接腰斩再打折;同一密度下前递全开(覆盖 95%),停顿项缩到 0.03 拍。Patterson Ch4 的量级感:若停顿占执行周期的 20%,CPI 从 1 恶化到 1.2,等效性能掉 17%。气泡之魔推荐「停一拍等它写完」没有任何技术错误——安全、稳妥、语义正确;错的是把它当唯一选项。编译器也能消气泡:指令调度(scheduling)把无关指令搬进停顿缝里填坑,gcc -O2 与硬件前递是同一笔账的两个付款人——软件填一拍省一份前递逻辑,硬件前递一劳永逸但付面积,谁付取决于目标市场的时钟与面积预算。

详解 · K12-13 流水线冒险检测的硬件实现

冒险检测单元(hazard detection unit)在 Patterson Ch4 的五级机里是一小段纯组合逻辑,驻在 ID 级:拿 ID 中指令的源寄存器号(rs/rt)与 EX/MEM、MEM/WB 两组级间寄存器里的目的寄存器号(rd)逐位比对,命中且目的寄存器非 $zero 即触发——2 个源 × 2 个目的 × 5 位 = 20 根比较线,加上使能逻辑。触发后的动作分两路:普通 RAW 且可前递 → 零开销,交给前递单元给 ALU 输入端的多路选择器选路;Load-use 死角(EX/MEM 级是 LW 且其目的寄存器命中本指令源寄存器)→ 必须停顿:PCWrite 与 IF/ID Write 拉低一拍,同时把 ID/EX 的控制信号清零注入气泡。S5 每拉一次密度旋钮,时空图里冒出的灰格子就是这段逻辑逐拍逐指令的判决结果。工程要点:这段比较逻辑本身悬在关键路径上(它必须在 EX 开门前出结果),深流水线里它的放大版——乱序机的寄存器重命名表 CAM 匹配——是调度电路功耗的大头。第一戒律是保守:宁可多停一拍,不可错放一个冒险;检测电路判错一次,架构状态就被污染,那是不可恢复的正确性事故,而多停一拍只是性能账。

第二幕 · 重建

前递之桥 · 数据前递

不等写回,直接把结果递过去

气泡深渊的灰雾还没散尽。诺依曼老祖在法阵旁画了一条弧线——从EX层的出口,直接弯回EX层的入口。你看着那条弧线,像一座桥,跨过了三拍的等待。

诺依曼老祖 诺依曼老祖

「ADD的结果在EX就算完了。为什么要等它走完MEM、走完WB、写回寄存器堆,再让SUB去读?直接从EX递给EX——不等写回。」

前递路径架设器 · EX→EX / MEM→EX

架设两条前递路径 → 气泡消散 → 触发Load-use死角

CPI = 1.500 气泡消除 = 0
详解 · K12-06 数据前递(Forwarding/Bypassing)技术

前递的思想一句话:结果从哪算出来就从哪递走,不等它绕道寄存器堆。五级机两条经典路径:EX→EX(相邻 RAW——ADD 在第 3 拍 EX 末就算完,值挂在 EX/MEM 级间寄存器上,SUB 第 4 拍进 EX 时直接从那里拿)与 MEM→EX(间隔 1 拍的 RAW——从 MEM/WB 级间寄存器递)。S6 架桥的账本可验算:两条桥架完 CPI 1.50→1.25→1.05,九成 RAW 被覆盖。硬件代价清单:ALU 两个输入端各加一个多路选择器(前递值 vs 寄存器堆读出值),加一段比较选路逻辑——面积开销个位数百分比,换来停顿近乎归零,是流水线史上性价比最高的单项投资。Patterson Ch4 划定的能力边界:前递覆盖一切「ALU 结果→ALU 输入」的依赖,但覆盖不了「访存结果→立即使用」(S6 的 Load-use 死角)与个别更远距离的组合。实现里的坑:前递必须旁路掉 ID 级读寄存器堆的动作,比较逻辑挂在 EX 段入口、只有半拍时序预算;且随流水线加深,前递网络的源与汇都在增殖——20 级机的前递多路器树本身就是时序瓶颈与功耗大户,这正是 S10 深度曲线里「级间开销吃掉切分红利」的物理来源。前递救得了数据冒险,救不了控制冒险——S7 见。

详解 · K12-11 Load-use冒险的特殊处理

Load-use 是前递版图上唯一的死角:LW R1,0(R2) 的数据要到 MEM 级(第 4 拍)末才从 D-Cache 出来,而紧随其后的 ADD R3,R1,R4 在第 4 拍初进 EX 就需要 R1——数据晚到一拍,前递弧线搭不过去,S6 触发按钮后断掉的那道弧就是这个时序错位。标准处理就是认赔 1 拍:冒险检测单元在 ID 级识别「EX/MEM 是 LW 且目的寄存器命中本指令源寄存器」,停顿一拍把使用者推迟,之后 MEM→EX 前递接力送达。量化:S5 实验台 Load-use 旋钮每 +5%,CPI +0.05;典型整数程序 Load-use 频率约 5%~8%,贡献 CPI 恶化 0.05~0.08。三条根治路线各有代价:编译器调度(把一条无关指令塞进 LW 与使用者之间——前提是找得到,寄存器压力大时未必有);硬件互锁(interlock,自动插泡——初代 MIPS 反其道而行,ISA 名字本义就是「无互锁流水线阶段的微处理器」,把责任推给编译器,从 MIPS II/R4000 起才补上互锁);加深访存流水(把 MEM 拆成两拍给 D-Cache 喘息——这正是 L1 命中延迟 1~2 拍的由来,但加深又加重分支冲刷,S10 的权衡曲线再添一项)。1 拍看似不多,但它是「前递万能论」的活体反例,也是 ch13 内存消歧(memory disambiguation)问题的起点。

第二幕 · 再崩

分岔迷途 · 控制冒险

分支猜错了——冲刷

前递之桥架好了,法阵流畅了许多。你正觉得胜利在望——时空图上出现了一条 BEQ R1, R2, Label 指令。它在EX层比较R1和R2,如果相等就跳转。但IF层已经提前取了指令。取的是谁?不知道。

气泡之魔 气泡之魔

「猜吧。猜对了什么事都没有。猜错了——冲刷。」

控制冒险体验器 · 冲刷代价

运行分支序列 → 预测错误冲刷 → 双滑块量化 → 延迟槽

冲刷开销CPI = 0.09 总CPI = 1.09 加速比 S = 4.59
详解 · K12-08 控制冒险(Control Hazard):分支延迟槽与分支预测

控制冒险的根源:分支的方向与目标要到 EX(第 3 拍)才见分晓,但 IF 每拍都在取下一条——取到的可能是错路的指令。五级机的损失账:分支解析前的 2 拍里已流入 2 条错路指令,猜错即冲刷(flush)。S7 双滑块的可验算公式:冲刷CPI = 分支频率×预测错误率×2,默认 15%×30%×2=0.09,总 CPI 1.09、S=4.59;若完全不预测(错误率 50%),CPI 恶化到 1.15。三条对策按代价排序:早解析——把分支比较从 EX 挪到 ID,损失从 2 拍砍到 1 拍,代价是 ID 级加一套比较器、还得配一条专用前递路径喂数据给它;延迟槽(delay slot,MIPS 的选择)——分支后那条指令无论跳不跳都照常执行,编译器负责找一条「必有指令」填进去,填得好猜错零代价,填不上只能塞 NOP,典型代码填充率约六成,本质是把代价从硬件转移给编译器;预测(S8 的战场)——赌历史会重演,用准确率换冲刷。冲刷的硬件实现与异常处理同源:把 IF/ID、ID/EX 的控制信号清零变 NOP,流水线自己格式化自己。控制冒险的特殊地位:冲刷损失随流水线深度线性增长——S10 那台 31 级机错一次冲 31 拍,它是深流水线的头号杀手。

第三幕 · 突破

预言之眼 · 分支预测

从静态到两级自适应——准确率飙到95%

法阵上方浮出一只石眼,眼眶里空空荡荡。诺依曼老祖说:冲刷的代价取决于你猜得准不准。这只眼,可以帮你猜。但你得自己教它。

气泡之魔 气泡之魔

「猜?你猜得了几次?历史不会简单重复。」

分支预测器配置面板

依次配置预测器 → 看准确率与冲刷代价联动

准确率 = 50% 残余冲刷CPI = 0.15 加速比 S = 4.35
详解 · K12-09 静态分支预测与动态分支预测(BHT/BTB)

静态预测在编译期定死策略、运行期不改:Always Not Taken 零硬件,S8 面板实测约 50%;BTFN(向后跳当作循环体 Taken、向前跳当作 if 未命中 Not Taken)利用统计先验抬到约 65%——这是零硬件预算时的底线,也解释了为什么它至今仍活在编译器的静态提示位(ISA 的 branch hint)里。动态预测把历史搬进硬件,两件套分工明确:BHT(Branch History Table,分支历史表)管方向——按 PC 低位索引,每项放 1 位或 2 位饱和计数器;BTB(Branch Target Buffer,分支目标缓冲)管目标——用全相联的 tag 匹配 PC,直接缓存跳转地址,让 IF 级当拍取到正确目标,S8 面板里 BHT+BTB 组合把准确率抬到 92%。1 位计数器的死穴在循环:TTTT…N 的模式里,退出必错一次、再进又错一次,「只记上次」系统性吃亏;2 位饱和计数器(strongly/weakly taken/not-taken 四态)连错两次才翻转,偶发抖动免疫,循环场景约 90%。Hennessy Ch3 的 SPEC89 实测:4096 项 2 位 BHT 的误预测率多在个位数到 12% 区间,容量再翻倍收益趋平——分支总数有限,表够大就饱和。工程权衡:BHT/BTB 按 KB 计费,索引冲突(两条分支映射同项)还会互相污染历史——更精细的解法见两级自适应。

详解 · K12-10 两级自适应分支预测器

两级自适应(two-level adaptive,Yeh & Patt 1991)的核心洞察:分支行为不是独立的掷硬币,而是有模式的序列——循环是 TTTT…N,奇偶交替是 TNTN…。只记「上次跳没跳」等于把序列当独立事件,注定丢模式。两级方案的分工:第一级 BHR(Branch History Register,n 位全局历史移位寄存器)记「最近 n 次的方向」;第二级 PHT(Pattern History Table,2^n 项)按 BHR 的模式值索引,每项是独立的 2 位饱和计数器——同一个分支的「TTT 之后」与「TNT 之后」各有各的计数器,模式不再互踩。S8 面板那个「3跳1不跳」的循环:BHR 学会 TTTN 模式后,除模式切换瞬间外全对,准确率 95%——20 次分支才错 1 次,残余冲刷CPI = 15%×5%×2 = 0.015,加速比回到 4.93。代价与坑:PHT 容量 = 2^n 项,历史位从 4 加到 12 容量翻 256 倍;全局历史会让不同分支互相污染(A 分支的 TTN 记到 B 分支头上),gshare(McFarling 1993)用 PC 与 BHR 异或哈希缓解;模式切换(循环退出、相位变化)必然连错两三次。现代 CPU 的 TAGE 类预测器已把准确率推到 97%~99%,CBP 分支预测冠军赛的前沿仍在抠最后 1%——S9 的气泡账提醒你:这最后 1%,每一拍都是钱。

第三幕 · 最高潮

Boss 战 · 气泡之魔

三问清算 · 排除法收网

法阵灯光齐灭。气泡从四面八方涌来,凝成一张巨大的、由无数灰色气泡拼成的脸。

气泡之魔 气泡之魔

「我是气泡之魔。我不住在流水线里——流水线是我的房子,不是我的食物。我住在'理想与现实的差距'里。你每以为5级就是5倍,我就胖了一圈。三问。答错一题,气泡就厚一分。」

渊账累计 0 笔 · 三问全对后清零

详解 · K12-12 流水线的异常与中断处理

异常(exception,同步、由指令引起:除零、溢出、页缺失、非法 opcode)与中断(interrupt,异步、由外设引起:时钟、I/O)在流水线里是同一件事:PC 被外部力量突然改道。难点在「账要算清」——精确异常(precise exception)的要求:异常指令之前的所有指令全部完成并提交、异常指令及其后的所有指令如同从未执行、作案 PC 存入 EPC(Exception PC)、控制权交给固定入口(MIPS 是 0x80000180)。流水线里的实现与 S7 的分支冲刷同源:检测到异常的那一级(溢出在 EX、页缺失在 MEM)拉高异常标志,让它随指令沿流水线下传,到提交点时把后继指令全部冲成 NOP——S9 气泡账的严苛之处正在这里:每个气泡都必须记到正确的那条指令头上,多记一笔、少记一笔,程序语义就崩。乱序执行让精确异常变得昂贵:指令乱序完成,谁的异常先报?ch13 的 ROB(重排序缓冲)按序提交就是为了保住精确异常——推测执行的指令先不提交,异常挂起等轮到它。历史反例:CDC 6600 与早期向量机的不精确异常(imprecise exception)让操作系统无法恢复现场,Hennessy Ch3 把它列为乱序机必须解开的死结之一。中断还多一层麻烦:异步事件要挑一个指令边界插入,通常在级间寄存器打一拍快照——异常是这条指令的错,中断是谁的错都不是,账本必须分开记。

第三幕 · 收束

尾声 · 性能天平

更深还是更宽?——这是下一章的问题

气泡散尽时,法阵恢复了运转。你的本命流水线上还留着几道疤痕——结构冒险修好了、大部分数据冒险被前递覆盖了、Load-use那道还在、控制冒险被预测压到最低。

诺依曼老祖 诺依曼老祖

「你算出了4.7倍。不错。但——如果我把5级改成10级呢?20级?31级?Pentium 4,31级流水线,主频飙到3.8GHz——但一次分支预测错误冲刷31拍。更深,还是更宽?——这是下一章的问题。」

知识图谱点亮 · Pentium 4 深度权衡

点亮全章碎片 → 拖深度滑块看主频与冲刷代价交叉

详解 · K12-14 流水线设计的性能-复杂度权衡

深度的收益:k 级流水线理论上每级工作量 1/k,主频可提近 k 倍——但有三重折扣:级间寄存器的 setup/hold 开销每级照付(每级 1~2 个 FO4 延迟,级数越多占比越大,收益递减);不平衡的级划分让 max(t_stage) 卡死时钟;前递网络与冒险检测逻辑随深度急剧变复杂(20 级机的前递比较要跨十几级)。深度的代价:分支冲刷损失 ∝ 深度。S10 双曲线的交点在 k≈20:freqGain(20)=flushCost(20)≈2.80——过了这点,加深净亏。真实案例复盘:Pentium 4 Willamette 20 级冲上 2GHz、Prescott 31 级冲 3.8GHz,赌的是「频率换 IPC 损失」;结果一次分支预测错误冲 31 拍,深前递网络又吃掉大量面积功耗,NetBurst 架构止步 3.8GHz,Core 微架构掉头回到 14 级、靠每周期 4 发射的宽度取胜——更深输给了更宽,S10 结尾那句「这是下一章的问题」正是 ch13 超标量的入口。此后十几年的主流高性能核大多停在 14~20 级,这是业界用真金白银投出来的平衡点。设计方法论收束:先量化冒险的 CPI 账(S3~S8 五幕就是账本),再选深度使「频率收益 − 冲刷代价 − 级间开销」最大——深度不是美德,是待优化的变量。

复盘 · 流水贯脉主线:从5倍幻觉到4.7倍真账

一条主线串起全部场景:概念赌局(S0:n+k−1 的充满开销,10 条指令 14 拍)→ 五级法阵(S1:IF/ID/EX/MEM/WB 与四组级间寄存器)→ 理论之巅(S2:S=kn/(n+k−1),n=1000 时 4.98)→ 第一崩塌(S3:结构冒险,I/D 分体用面积买断)→ 依赖之网(S4:RAW 真依赖 vs WAR/WAW 假依赖)→ 第二崩塌(S5:停顿与气泡,前递关死、密度 30% 时 CPI 1.60、S 3.13)→ 前递之桥(S6:EX→EX 与 MEM→EX 覆盖九成 RAW)→ 分岔迷途(S7:控制冒险,15%×30%×2=0.09 CPI)→ 预言之眼(S8:两级自适应 95%,残余 0.015)→ 总清算(S9:气泡账清零——不是消灭,是记账)→ 性能天平(S10:k≈20 交点,更深输给更宽)。三幕崩塌各立一道界碑:结构冒险花钱买得断、数据冒险前递覆盖九成但 Load-use 认赔 1 拍、控制冒险只能压不能消——条条不是「消灭」,全是权衡。最终账本:CPI = 1 + 0.05(Load-use)+ 0.015(分支残余)≈ 1.065,S ≈ 4.7——4.7 倍不是 5 倍的残次品,是理想与现实的差距被亲手填平后的真实数字。结丹一层圆满;下一问已悬在梁上:更深还是更宽?ch13 并指令渊。

交互实验

流水线吞吐与冒险

调节级数与冒险率,对比理想与含冒险下的完成周期。