并指令渊
你以为每周期发射8条指令就是8倍加速。但编译器看不见运行时的依赖,真依赖链是计算的因果律,推测执行的脚印会泄露密码。ILP有天花板——风不够大,翅膀再宽也白搭。
并行之渊
并指令之渊的入口是一道裂缝——不是门,是大地被某种力量撕开的伤口。裂缝深处,无数指令的光流像萤火虫一样涌动,有的并行飞舞,有的却被一条条冷铁锁链拴在一起,只能排队通过。
「你在ch12学会了流水线——每个周期流过一条指令。但你看到了吗?那些没被锁链拴住的萤火,本可以一起飞。」
「8条一起飞,8倍。你说是不是?」
ILP潜力预测挑战器
先下注 → 拖依赖分析镜扫指令 → 拖窗口滑块看ILP极限
依赖分析镜 · 扫描6条指令
超标量法阵 · 多发射
裂缝深处是一座半成型的法阵——五级流水线的骨架还在,但只有一个发射口,每周期只放一条指令进来。诺依曼老祖递给你一组功能石。
「你的ch12法阵只有一个门。现在,给它开四个门——每周期放四条指令进来。ALU、Load/Store、乘除法器、分支单元,各一块。」
「四扇门开了。但它们排好队了吗?」
超标量发射流水线动画器
拖功能石到发射槽 → 切静态/动态模式 → 测CPI
VLIW迷殿 · 编译器之梦
迷殿中央悬浮着一块巨型晶板——上面刻的不是单条指令,而是一个"指令束":五条指令被编译器打包成一行的超长指令字,硬件只需照单全收。你看着那些整齐的指令束,觉得完美——直到你尝试编译一段含分支的循环。
「编译器能看穿一切吗?它知道分支会走哪边吗?它知道Cache命中不命中吗?它知道两个指针是否同一地址吗?」
VLIW指令束打包推演器
选代码编译打包 → 拖盲区标签触发nop → 拖Itanium时间轴到2021
Itanium时间轴 · 2001→2021
「排除其一:编译器即全能,不通。」
乱序之渊 · 破序而出
VLIW迷殿崩塌的碎石还未落定,裂缝深处又亮起一道光——那里没有编译器,没有指令束,只有一片混沌的指令流。指令萤火不再排队——它们各自查看自己的操作数,准备好了就飞向功能单元,不管程序里谁先谁后。
「编译器站在编译时,看不见运行时。那谁站在运行时?——硬件自己。先就绪的先执行,后就绪的后执行。顺序乱了,但吞吐量涨了。」
你忽然发现一个问题:「如果第5条指令先执行完了,第3条还没开始,发生异常时怎么办?程序序在哪里?」
乱序执行模拟器 · 顺序/乱序对比
运行8条指令 → 切换顺序/乱序对比 → 触发异常看精确异常问题
保留站 · Tomasulo
渊底出现一排石台——每个石台对应一个功能单元,石台上刻着"保留站"三个字。一条公共数据总线(CDB)从功能单元的出口延伸到所有保留站——结果算出来后,沿CDB广播到所有正在等它的保留站。
「指令不能直接冲向功能单元——它得先在保留站等着。操作数齐了,就从保留站发射出去;没齐,就留在站里,不挡后面的路。候车室——操作数没到就坐着等,不站在门口挡路。」
「保留站装得下多少?装不下的呢?」
Tomasulo保留站三旋钮实验台
调三旋钮 → 运行8条指令 → 触发异常看冲刷
重命名之镜 · 真依赖现形
重命名之镜悬浮在渊底中央,镜面映出无数寄存器的影子——但有些影子重名了。I1写R1,I5也写R1。它们写的不是同一个"东西",只是碰巧叫同一个名字——这就是WAW假依赖。你伸手把镜中重名的R1影子分开,锁链断了。
「你消掉了名字的冲突。很好。但我的锁链——从A的结果到B的输入——你消不掉。B需要A算出来的值。不是名字,是值。你能让B在A算完之前就知道结果吗?」
三类依赖识别 + 寄存器重命名引擎
识别RAW/WAR/WAW → 启动重命名消假依赖 → 对RAW重命名看报错
重命名映射表 · 架构→物理
重排序缓冲 · 精确异常
渊底浮起一个巨大的环形石盘——上面刻着一行行槽位,像一条首尾相连的传送带。指令按程序序进入槽位,乱序执行的结果暂存在各自的槽位里,但只有排到队首的才能"提交"——写回架构寄存器,对外可见。
「异常发生时呢?冲刷——把ROB里未提交的条目全部清空。已提交的是精确状态,未提交的当没发生过。乱序执行是引擎,ROB是刹车——没有刹车的引擎不是高性能,是事故。」
ROB环形队列 · 乱序执行按序提交
运行指令看乱序完成 → 按序提交 → 触发异常看冲刷恢复
推测之眼 · 投机执行
渊壁上睁开了一只巨大的眼——瞳孔是分支指令,虹膜是两条分支路径。你让指令萤火飞过分支——它们带着"投机"标记飞入功能单元。结果暂存在ROB里,不提交,等分支结果出来。依赖之锁沉默地看着——这一次它没有嘲讽。它的锁链在分支处断开了,因为推测执行绕过了控制依赖。但它的沉默比嘲讽更不安。
「ch12的流水线每个周期只流过一条指令,预测错了冲刷几拍。现在你的超标量每周期发射4条——预测错了冲刷的是4条的倍数。多发射对分支预测更敏感。」
推测执行模拟 · IPC矩阵 · 微架构脚印
启动推测执行 → 调发射宽度×准确率看IPC → 打开脚印透视
发射宽度 × 预测准确率 → IPC
微架构状态透视镜
幽灵之祸 · Spectre
渊底温度骤降。一面巨大的屏幕从黑暗中浮出——上面是一个密码数组的内存布局图。2018年1月,Google Project Zero和几支学术团队同时披露了Spectre和Meltdown。推测执行——你刚学会的性能利器——成了一个后门。攻击者把x设成越界值,指向内核密码的地址。推测执行越过边界检查,读出了密码的值。
「那条投机指令最终被冲刷了——但array2中对应密码值的那一行被加载进了Cache。脚印留下来了。你用Flush+Reload侧信道测量访问时间,就能反推出密码。」
Spectre攻击模拟 · 侧信道提取 · 回滚对比
发动攻击越界读密码 → 侧信道计时反推 → 看架构/微架构恢复对比
状态回滚对比
Boss 战 · 依赖之锁
幽灵之祸的红色脉冲还未消退,全屏骤然压暗。冷铁锁链从四面八方涌来,编织成一张巨大的网——网上挂着你这一路留下的每一笔锁链账。锁链凝聚成人形——比S5那次更清晰,更庞大。它没有面孔,但你感觉到它在审视你。
「我是依赖之锁。我不住在假依赖里——那些你用重命名消掉的,从来不是我的本体。我不住在控制依赖里——那些你用推测执行绕过的,只是我的影子。我住在真依赖里——B需要A算出来的值。三问。答错一题,锁链就紧一分。」
渊账累计 0 笔 · 三问全对后清零
尾声 · 软硬协奏
锁链散尽时,渊底亮了起来。你的本命发射器上还留着几道锁链的压痕——VLIW的nop空泡修了、假依赖消了、真依赖链接纳了、Spectre的脚印知道了。诺依曼老祖递给你一段循环代码:硬件做到极限了,但你看这段循环——如果展开4次呢?
「软件流水线更精妙——不同迭代的指令交错排布,就像ch12的流水线让不同指令重叠一样。锁链还在,但被打散了。但——你有没有想过,ILP榨干了,下一个瓶颈在哪?指令再快,数据供不上怎么办?下一章,我们去看数据的家——存储器系统。」
知识图谱点亮 · 循环展开双曲线
点亮全章碎片 → 拖展开因子看ILP/代码体积交叉