VLIW 的核心哲学
编译器保证并行性和数据就绪,硬件不做运行时依赖检查
CS152 · 第 14 课
乱序执行的控制逻辑非常昂贵——一张 MIPS R10000 的裸片照片上,"Control Logic"区域占据了相当大的面积。这一课提出一个针锋相对的设计哲学:如果编译器已经能找出指令间的并行性,为什么还要让硬件在运行时重新做一遍同样的依赖检查?
"超标量编译器"流程:找出独立的操作 → 调度这些操作 → 仍然生成顺序的机器代码。"超标量处理器"流程:检查指令依赖 → 调度执行——这一步是在硬件里、运行时、重复地做编译器早就做过的工作。VLIW(Very Long Instruction Word,超长指令字)的提出正是要打破这种重复劳动:把多个操作打包进一条指令,每个操作槽固定映射到一个功能单元,操作延迟在编译期就是已知常数;编译器必须保证(a)同一条指令内的操作彼此并行、无冲突(硬件不需要做跨操作的 RAW 检查),以及(b)数据在被使用前一定已经就绪(硬件不需要任何数据互锁电路)。
这一思想最早由 Fisher、Ellis、Ruttenberg、Nicolau 在耶鲁大学提出("Parallel Processing: A Smart Compiler and a Dumb Machine," SIGPLAN '84),配套的 ELI(Enormously Long Instructions)机器项目由此得名。早期真实 VLIW 机器:FPS AP120B(1976,第一台商用宽指令机器,手工编写向量库,用软件流水线和循环展开);Multiflow Trace(1987,商业化了 Fisher 团队的"trace scheduling"思想,配置 7/14/28 个操作/指令,28 个操作打包进 1024 位指令字);Cydrome Cydra-5(1987,256 位指令字编码 7 个操作,带旋转寄存器堆,由 Bob Rau 创立)。
以 for(i=0;i<N;i++) B[i]=A[i]+C; 为例,使用一个 6 槽的 VLIW 模板(Int1、Int2、M1、M2、FP+、FPx):
fadd → 1/8 = 0.125 FLOPS/周期(重新验算:1÷8=0.125,与幻灯片一致)fadd → 4/11 ≈ 0.36 FLOPS/周期(重新验算:4÷11=0.3636…,四舍五入到 0.36,与幻灯片一致)fadd → 4/4 = 1 FLOPS/周期(重新验算:4÷4=1,与幻灯片一致)循环展开 vs. 软件流水线的本质区别:循环展开的启动/收尾开销要在每一组展开的迭代里重复支付;软件流水线则把这份开销压缩到只需要为整个循环支付一次——这正是软件流水线能把吞吐率从 0.36 进一步提升到理论最优值 1 的原因。
循环展开还需要注意:展开程度受架构寄存器数量限制;会增大静态代码体积(NOP 填充+代码复制);需要处理 N 不是展开因子倍数时的收尾循环。当代码里没有规整的循环(分支密集的不规则控制流限制了基本块大小)时,Trace Scheduling(Fisher, Ellis)通过 profiling 反馈或编译器启发式,挑出最常见的分支路径(一条"trace")整体调度,再为偏离 trace 的分支插入修补代码。
EPIC(Explicitly Parallel Instruction Computing)是 Intel 给 IA-64 起的架构风格名字——幻灯片直言"本质上就是 VLIW"。Merced 是第一代 Itanium 实现(原计划 1997 年首批出货,实际到 2001 年才真正出货);McKinley(第二代)2002 年出货;Poulson(2011)8 核,32nm 工艺,超过 30 亿晶体管。IA-64 用 128 位的"束(bundle)"打包 3 条指令+1 个模板字段,模板位编码指令如何与相邻束组成可以跨越 bundle 边界的"并行执行组";提供 128 个 64 位通用整数寄存器、128 个浮点寄存器、64 个 1 位谓词寄存器,且通用寄存器支持"旋转"(同一条静态指令在不同循环迭代访问不同的物理寄存器,是一种轻量级、编译器/硬件协作的重命名机制,专为压缩软件流水线循环的代码体积而设计)。
IA-64 的三项关键 ILP 增强技术:
Load.s/Chk.s):让一条推测性 load 可以被提到分支之前——它自己永远不会抛出异常(只是给目标寄存器打一个"中毒"标记),原来的位置留一条检查指令 Chk.s,只有真正执行到那里且被标记中毒时才跳转到修补代码。Load.a/Load.c):让一条 load 可以被提到一条地址未知、可能别名的 store 之前——把地址记录进一张"地址检查表",之后如果有 store 写入匹配的地址就使该记录失效;Load.c 检查指令负责发现失效并跳转到修补代码。限制因素:静态不可预测的分支;可变的访存延迟(缓存缺失无法预知);代码体积膨胀;编译器复杂度过高。幻灯片的结论直白:"尽管多次尝试,VLIW 在通用计算领域还是失败了"——更复杂的 VLIW 设计最终在复杂度上逼近了乱序超标量,却在大型复杂应用上拿不出真正的优势。Transmeta(动态把 x86 翻译成内部 VLIW,首任 CEO Dave Ditzel)是一种混合尝试。VLIW 在嵌入式 DSP 市场却取得了成功(更简单的 VLIW 设计、更受限/更友好的代码),幻灯片明确指出:Google 的 TPU(张量处理单元)本质上就是一台 VLIW 机器。
幻灯片引用了 Donald Knuth 的评价:"……Itanium 那套本来被吹得神乎其神的方案——直到人们发现,原本期待中的编译器根本不可能被写出来。"根据 Wikipedia 的说法,Intel 已于 2019 年 1 月 30 日正式宣布 Itanium 停产。
问题 1:VLIW 架构要求编译器必须"保证"哪两件事,从而让硬件不再需要做运行时的依赖检查?
问题 2:为什么软件流水线能把吞吐率从循环展开的约 0.36 FLOPS/周期进一步提升到 1 FLOPS/周期?
问题 3:根据本课内容,VLIW 架构的真实历史命运是什么?
编译器保证并行性和数据就绪,硬件不做运行时依赖检查
0.125 → 0.36 → 1 FLOPS/周期,开销支付方式不同
谓词化执行、控制推测、数据推测
通用计算失败(Itanium),DSP/TPU 领域成功
sp23/lecture_slides/L14_VLIW.pdf(UC Berkeley CS152/CS252A, Spring 2023, Sophia Shao)