并指令渊
你以为每周期发射8条指令就是8倍加速。但编译器看不见运行时的依赖,真依赖链是计算的因果律,推测执行的脚印会泄露密码。ILP有天花板——风不够大,翅膀再宽也白搭。
并行之渊
并指令之渊的入口是一道裂缝——不是门,是大地被某种力量撕开的伤口。裂缝深处,无数指令的光流像萤火虫一样涌动,有的并行飞舞,有的却被一条条冷铁锁链拴在一起,只能排队通过。
「你在ch12学会了流水线——每个周期流过一条指令。但你看到了吗?那些没被锁链拴住的萤火,本可以一起飞。」
「8条一起飞,8倍。你说是不是?」
ILP潜力预测挑战器
先下注 → 拖依赖分析镜扫指令 → 拖窗口滑块看ILP极限
依赖分析镜 · 扫描6条指令
详解 · K13-01 指令级并行(ILP)的概念与潜力
ILP 指一段指令流中可重叠执行的独立指令数。定量方法:把指令按真依赖连成 DAG,理论 ILP = 指令总数 ÷ 关键路径长度。本例 6 条指令、两条 RAW 链 I1→I2 与 I5→I6,关键路径 2 拍,ILP = 6/2 = 3。经验研究(Wall 1991)表明典型程序基本块内 ILP 仅 2~3.5——这就是"风不够大"的物理含义。
详解 · K13-14 ILP 的物理极限:窗口与依赖
硬件能提取的 ILP 受两件事钳制:指令窗口大小与真依赖链长度。窗口曲线 ILP(W) = ILP∞·(1−e^(−W/W₀)) 说明窗口扩大收益迅速饱和;而 ILP∞ 由程序的因果结构决定,无限窗口也突破不了。真依赖链是计算的因果律——它不在硬件里,在算法里。
超标量法阵 · 多发射
裂缝深处是一座半成型的法阵——五级流水线的骨架还在,但只有一个发射口,每周期只放一条指令进来。诺依曼老祖递给你一组功能石。
「你的ch12法阵只有一个门。现在,给它开四个门——每周期放四条指令进来。ALU、Load/Store、乘除法器、分支单元,各一块。」
「四扇门开了。但它们排好队了吗?」
超标量发射流水线动画器
拖功能石到发射槽 → 切静态/动态模式 → 测CPI
详解 · K13-02 超标量处理器:多发射机制
超标量每拍从指令流取 n 条,经相关性检查后分发到多条功能单元并行执行。理想 CPI = 1/n(4 发射即 0.25),但实际受依赖、分支、功能单元冲突拖累——本章 4 发射跑出 CPI=0.4,只用了理论峰值的六成。发射宽度每 +1,分发逻辑的比较器数量按 O(n²) 增长,面积功耗非线性膨胀。
详解 · K13-03 静态多发射 vs 动态多发射
静态多发射:编译器在编译期排好并行包(VLIW/EPIC),硬件只照单执行——硬件简、但看不见运行时。动态多发射:硬件在运行期用记分牌/Tomasulo 现场决定谁并行——能适应 cache miss 等动态事件,代价是复杂的调度逻辑。现代高性能 CPU 几乎全走动态路线,静态路线退守 DSP 与嵌入式。
VLIW迷殿 · 编译器之梦
迷殿中央悬浮着一块巨型晶板——上面刻的不是单条指令,而是一个"指令束":五条指令被编译器打包成一行的超长指令字,硬件只需照单全收。你看着那些整齐的指令束,觉得完美——直到你尝试编译一段含分支的循环。
「编译器能看穿一切吗?它知道分支会走哪边吗?它知道Cache命中不命中吗?它知道两个指针是否同一地址吗?」
VLIW指令束打包推演器
选代码编译打包 → 拖盲区标签触发nop → 拖Itanium时间轴到2021
Itanium时间轴 · 2001→2021
「排除其一:编译器即全能,不通。」
详解 · K13-04 VLIW(超长指令字)架构原理
VLIW 把多条操作打包进一条超长指令(128 位甚至更宽),每个槽位固定对应一个功能单元,并行性完全由编译器静态指派。省掉了硬件相关检测与乱序调度,译码器极简。软肋:cache miss 延迟不可静态预测、二进制不兼容(换宽度就换指令集)、代码体积因 NOP 填充膨胀。
详解 · K13-05 EPIC 与 Intel Itanium 案例
EPIC(显式并行指令计算)是 VLIW 的改良:128 位包内含 3 条 41 位指令 + 5 位模板,配谓词寄存器与编译器控制的推测(ld.s/ld.a)来弥补静态调度的盲区。Itanium 2 曾拿下 SPEC 性能王座,但 x86 二进制兼容缺失 + 编译器难度超预期 + AMD64 反向兼容路线夹击,Intel 于 2021 年终止产品线——"编译器全知"假说的世纪实验就此落幕。
乱序之渊 · 破序而出
VLIW迷殿崩塌的碎石还未落定,裂缝深处又亮起一道光——那里没有编译器,没有指令束,只有一片混沌的指令流。指令萤火不再排队——它们各自查看自己的操作数,准备好了就飞向功能单元,不管程序里谁先谁后。
「编译器站在编译时,看不见运行时。那谁站在运行时?——硬件自己。先就绪的先执行,后就绪的后执行。顺序乱了,但吞吐量涨了。」
你忽然发现一个问题:「如果第5条指令先执行完了,第3条还没开始,发生异常时怎么办?程序序在哪里?」
乱序执行模拟器 · 顺序/乱序对比
运行8条指令 → 切换顺序/乱序对比 → 触发异常看精确异常问题
详解 · K13-06 乱序执行的基本思想
顺序发射遇到一条被阻塞的指令就全线停摆;乱序(OoO)让就绪的指令先走:取指译码按程序序,发射看操作数就绪与否,完成顺序任意。收益:cache miss、长延迟运算不再卡住整条流水线。代价:必须重建"对外的顺序假象"——异常精确性、内存一致性都要求最终按程序序可见,这正是 ROB 存在的理由(S6)。
保留站 · Tomasulo
渊底出现一排石台——每个石台对应一个功能单元,石台上刻着"保留站"三个字。一条公共数据总线(CDB)从功能单元的出口延伸到所有保留站——结果算出来后,沿CDB广播到所有正在等它的保留站。
「指令不能直接冲向功能单元——它得先在保留站等着。操作数齐了,就从保留站发射出去;没齐,就留在站里,不挡后面的路。候车室——操作数没到就坐着等,不站在门口挡路。」
「保留站装得下多少?装不下的呢?」
Tomasulo保留站三旋钮实验台
调三旋钮 → 运行8条指令 → 触发异常看冲刷
详解 · K13-07 Tomasulo 算法:保留站与重命名
Tomasulo(1967,IBM 360/91)三件套:保留站——每个功能单元前的等候区,指令取到操作数立即执行,不再按序阻塞;寄存器重命名——保留站编号取代体系结构寄存器名,WAW/WAR 假依赖自动消解;CDB 公共数据总线——运算结果一次广播,所有等待该值的保留站同拍捕获。数据流驱动取代控制流驱动,是乱序执行的数学原型。
重命名之镜 · 真依赖现形
重命名之镜悬浮在渊底中央,镜面映出无数寄存器的影子——但有些影子重名了。I1写R1,I5也写R1。它们写的不是同一个"东西",只是碰巧叫同一个名字——这就是WAW假依赖。你伸手把镜中重名的R1影子分开,锁链断了。
「你消掉了名字的冲突。很好。但我的锁链——从A的结果到B的输入——你消不掉。B需要A算出来的值。不是名字,是值。你能让B在A算完之前就知道结果吗?」
三类依赖识别 + 寄存器重命名引擎
识别RAW/WAR/WAW → 启动重命名消假依赖 → 对RAW重命名看报错
重命名映射表 · 架构→物理
详解 · K13-09 寄存器重命名:消除 WAW 与 WAR
三类数据依赖中:RAW(写后读)是真依赖——后指令需要前指令算出的值,任何技术都无法消除,只能等待;WAW(写后写)与 WAR(读后写)是名字依赖——两条指令碰巧用了同一寄存器名,值上毫无关系。重命名给每次写分配新物理寄存器(现代 CPU 有数百个,如 Golden Cove 有 332 个整数物理寄存器),假依赖随之消失。ILP 的终极上限因此只剩 RAW 链长。
重排序缓冲 · 精确异常
渊底浮起一个巨大的环形石盘——上面刻着一行行槽位,像一条首尾相连的传送带。指令按程序序进入槽位,乱序执行的结果暂存在各自的槽位里,但只有排到队首的才能"提交"——写回架构寄存器,对外可见。
「异常发生时呢?冲刷——把ROB里未提交的条目全部清空。已提交的是精确状态,未提交的当没发生过。乱序执行是引擎,ROB是刹车——没有刹车的引擎不是高性能,是事故。」
ROB环形队列 · 乱序执行按序提交
运行指令看乱序完成 → 按序提交 → 触发异常看冲刷恢复
详解 · K13-08 重排序缓冲(ROB)与精确异常
ROB 是一个按程序序排列的环形队列:指令译码后入队占位,乱序执行,结果先写 ROB 而非寄存器堆;只有到达队首且无异常才提交,结果才真正写回体系结构状态。异常处理因此变得简单:冲刷队首之后所有未提交条目,机器状态精确等于"异常指令之前"。现代 CPU 的 ROB 容量即指令窗口:Intel Golden Cove 512 项、Apple M1 约 630 项——窗口越大挖得越深,冲刷代价也越高。
推测之眼 · 投机执行
渊壁上睁开了一只巨大的眼——瞳孔是分支指令,虹膜是两条分支路径。你让指令萤火飞过分支——它们带着"投机"标记飞入功能单元。结果暂存在ROB里,不提交,等分支结果出来。依赖之锁沉默地看着——这一次它没有嘲讽。它的锁链在分支处断开了,因为推测执行绕过了控制依赖。但它的沉默比嘲讽更不安。
「ch12的流水线每个周期只流过一条指令,预测错了冲刷几拍。现在你的超标量每周期发射4条——预测错了冲刷的是4条的倍数。多发射对分支预测更敏感。」
推测执行模拟 · IPC矩阵 · 微架构脚印
启动推测执行 → 调发射宽度×准确率看IPC → 打开脚印透视
发射宽度 × 预测准确率 → IPC
微架构状态透视镜
详解 · K13-10 推测执行:分支预测与投机
分支平均占指令流 15%~25%,多发射机器每拍都可能遇到分支——不等结果就发射槽全空。预测器(两位饱和计数器 → TAGE 神经预测器,准确率 >97%)让处理器按猜测的路径先行执行;猜对则延迟全免,猜错则冲刷 ROB 中推测路径的全部指令并回滚。预期收益 = 命中率×省下的延迟 − 误击率×冲刷代价,只要预测够准,赌局就稳赚。
详解 · K13-16 多发射对分支预测的刚需
发射宽度 n 越大,分支后果越严重:4 发射机器每 5 拍就遇一个分支,误预测一次浪费的是 n 倍发射槽的工作量。这就是为什么宽发射与深流水线必须配顶级预测器——Intel/AMD 旗舰核心的 TAGE 类预测器在多数负载上把误击率压到 1% 以下,预测器面积已占前端相当大比例。ILP 榨得越狠,对"算无遗策"的依赖越深。
幽灵之祸 · Spectre
渊底温度骤降。一面巨大的屏幕从黑暗中浮出——上面是一个密码数组的内存布局图。2018年1月,Google Project Zero和几支学术团队同时披露了Spectre和Meltdown。推测执行——你刚学会的性能利器——成了一个后门。攻击者把x设成越界值,指向内核密码的地址。推测执行越过边界检查,读出了密码的值。
「那条投机指令最终被冲刷了——但array2中对应密码值的那一行被加载进了Cache。脚印留下来了。你用Flush+Reload侧信道测量访问时间,就能反推出密码。」
Spectre攻击模拟 · 侧信道提取 · 回滚对比
发动攻击越界读密码 → 侧信道计时反推 → 看架构/微架构恢复对比
状态回滚对比
详解 · K13-11 Spectre / Meltdown 攻击原理
2018 年公布的两类漏洞把推测执行的脚印变成武器:Meltdown 利用乱序执行在权限检查完成前先读了内核页,再通过 cache 侧信道把字节泄露出来;Spectre 训练分支预测器,诱使受害者代码推测执行一段"幽灵gadget",把秘密编码进 cache 状态。架构状态虽被精确回滚,cache/TLB/预测器等微架构状态不会——秘密就藏在这道裂缝里。几乎所有高性能 CPU 无一幸免。
详解 · K13-12 推测失败的恢复:状态回滚
推测路径上的指令在 ROB 中全部标记"推测",预测失败时按 ROB 顺序批量冲刷:物理寄存器映射表回滚到分支点检查点、发射队列清空、PC 重定向到正确路径。回滚只保证架构状态(寄存器、内存)精确,不管微架构状态(cache 行、TLB、预测历史)——后者不对外可见,却可被计时侧信道观测。修复代价高昂:部分缓解措施(如关闭推测、清空缓存分区)让性能倒退 5%~30%。
Boss 战 · 依赖之锁
幽灵之祸的红色脉冲还未消退,全屏骤然压暗。冷铁锁链从四面八方涌来,编织成一张巨大的网——网上挂着你这一路留下的每一笔锁链账。锁链凝聚成人形——比S5那次更清晰,更庞大。它没有面孔,但你感觉到它在审视你。
「我是依赖之锁。我不住在假依赖里——那些你用重命名消掉的,从来不是我的本体。我不住在控制依赖里——那些你用推测执行绕过的,只是我的影子。我住在真依赖里——B需要A算出来的值。三问。答错一题,锁链就紧一分。」
渊账累计 0 笔 · 三问全对后清零
尾声 · 软硬协奏
锁链散尽时,渊底亮了起来。你的本命发射器上还留着几道锁链的压痕——VLIW的nop空泡修了、假依赖消了、真依赖链接纳了、Spectre的脚印知道了。诺依曼老祖递给你一段循环代码:硬件做到极限了,但你看这段循环——如果展开4次呢?
「软件流水线更精妙——不同迭代的指令交错排布,就像ch12的流水线让不同指令重叠一样。锁链还在,但被打散了。但——你有没有想过,ILP榨干了,下一个瓶颈在哪?指令再快,数据供不上怎么办?下一章,我们去看数据的家——存储器系统。」
知识图谱点亮 · 循环展开双曲线
点亮全章碎片 → 拖展开因子看ILP/代码体积交叉
详解 · K13-13 ILP 的硬件成本:面积、功耗、验证
乱序超标量的每一倍 ILP 都要真金白银来买:发射宽度 n 的相关检测逻辑约按 n² 增长;ROB、保留站、物理寄存器堆全是多端口 SRAM,端口数随发射宽度线性上升,面积功耗超线性恶化;验证复杂度更是组合爆炸——乱序+推测的正确性证明是形式验证界最硬的骨头之一。 Pentium 4 的长流水线赌局失败(NetBurst 2004 年止步 3.8GHz),是"硬件成本吃掉 ILP 收益"的经典判例。
详解 · K13-15 软件流水线与循环展开
编译器能在硬件窗口之外再挖一层 ILP:循环展开把迭代体复制 U 份,迭代间独立指令暴露给调度器(本章曲线:ILP≈2U,但代码体积同步 ×U,I-Cache 压力骤增,过交叉点即浪费);软件流水线更精妙——把迭代 i 的访存、迭代 i−1 的运算、迭代 i−2 的写回交错编排在同一循环体内,锁链被时间打散。IA-64 的旋转寄存器、现代编译器的 modulo scheduling 都是这一思想。
复盘 · 并指令渊全图
全章知识链:ILP 潜力(K13-01/14)→ 超标量多发射(K13-02/03)→ VLIW/EPIC 静态路线(K13-04/05)→ 乱序执行(K13-06)→ Tomasulo 三件套(K13-07)→ 重命名消假依赖(K13-09)→ ROB 精确异常(K13-08)→ 推测执行与分支预测(K13-10/16)→ Spectre 安全代价(K13-11/12)→ 成本与软件出路(K13-13/15)。一句话:ILP 的上限写在程序的因果结构里,硬件与编译器只是在这个上限内来回腾挪。