编译器是软件世界里最沉默的基础设施之一。你写下 for i in range(10): print(i),背后有一套复杂的程序把你的意图翻译成处理器能理解的指令,并在翻译过程中悄悄地、系统地把代码优化得更快。这个翻译和优化的过程,它的数学基础和工程实践,在很大程度上是弗朗西丝·艾伦(Frances Allen)奠定的。
2006 年,艾伦成为 ACM 图灵奖历史上第一位女性得主。
破除误解:优化编译器不是"让代码跑得稍微快一点"
对非专业人士来说,编译器优化听起来像是一项锦上添花的技术改进——快个 10%、20%。这严重低估了其重要性。
在超级计算机和并行计算出现之前,没有优化编译器,就没有实用的数值科学计算。核武器模拟、天气预报、流体动力学——这些需要解几百万个方程的问题,每一个处理器时钟周期都是宝贵的。艾伦在 IBM 为这些场景开发的编译器技术,不是让程序"稍微快一点",而是让原本不可能在合理时间内完成的计算变成了可能。
现场:从小学教师到 IBM 研究员
艾伦 1932 年 8 月 4 日生于纽约州珀鲁(Peru,一个小镇,不是南美国家)。她在奥尔巴尼州立大学获得数学学士学位(1954 年),后在密歇根大学读研究生。
1957 年,她以临时教师的身份加入 IBM,原计划只工作几年,赚钱还学生贷款,然后回去教数学。然而,她遇到了 FORTRAN——那是她入职时 IBM 刚刚发布的新语言。她被分配到学习和推广 FORTRAN 的工作,随即发现自己对编译器技术着了迷。
她再也没有去教书。在 IBM 工作了整整 45 年(1957–2002)。
核心贡献一:程序流图与数据流分析
1960 年代,艾伦开始系统研究如何把一个程序的结构表示成数学对象,从而对其进行自动分析和优化。她的关键洞见是:程序的执行路径可以表示为控制流图(Control Flow Graph):
- 节点(Node):程序中的基本块(Basic Block)——一段顺序执行、没有分支跳转的指令序列。
- 有向边(Edge):从一个基本块到可能的下一个基本块的控制转移(如 if 语句的两个分支)。
在这个图上,可以进行数据流分析(Data Flow Analysis):追踪程序中每一个变量在各个程序点的定义(赋值)和使用,从而发现: - 这个变量在这里的值到底是什么时候赋进来的? - 这个变量的值会被后面的代码使用吗,还是赋了以后就被覆盖了?(如果不会使用,赋值可以删除) - 两个计算实际上是等价的吗?(可以合并)
艾伦 1966 年的研究报告《Program Optimization》首次为"编译器可以系统地分析与变换程序"给出概念框架,1970 年的《Control Flow Analysis》则把控制流分析钉成了标准方法,ACM 的图灵奖授奖词专门点了这两篇的名。数据流分析的本质是一组不动点方程:每个程序点的信息由它的前驱决定,反复迭代直到信息不再变化。艾伦的工作让这种迭代既有理论保证(必然收敛),又有工程上可承受的速度——她发展的区间分析(interval analysis)把控制流图归约为层层嵌套的"区间",按层次求解,而不必对整张图逐点迭代。1976 年她与科克合写的《一种程序数据流分析过程》,进一步把"定义—使用"关系的计算系统化。今天所有现代编译器(GCC、LLVM、Java JIT)内部都运行着这套框架的某种形式。
核心贡献二:循环优化
数值科学计算的核心是嵌套循环——对矩阵、向量做大量重复操作。艾伦在 IBM 研究的主要工作之一,是理解和自动优化循环结构:
循环不变量外提(Loop-Invariant Code Motion):把循环内每次迭代都计算同样结果的表达式移到循环外,只计算一次。这是最简单的循环优化,但能产生显著加速。
循环展开(Loop Unrolling):把循环体复制多份,减少循环控制开销,同时给处理器更多指令流水线并行的机会。
循环融合(Loop Fusion)与循环分裂(Loop Fission):把两个遍历同一数据的循环合并(提升缓存命中率),或把一个循环的不同部分分开(便于向量化)。
向量化(Vectorization):现代处理器有 SIMD(Single Instruction Multiple Data)指令,可以用一条指令同时操作多个数据(如把 8 个浮点数同时加到另外 8 个浮点数上)。编译器需要分析循环,判断是否可以安全地生成这类指令,这需要精确的数据依赖分析。
艾伦为 IBM 的 STRETCH 和 HARVEST 超级计算机开发了早期的循环优化编译器,这是 1960 年代最先进的科学计算系统。
核心贡献三:并行编译理论
1970 年代,随着多处理器系统出现,艾伦开始研究自动并行化(automatic parallelization):如何让编译器自动识别程序中可以并行执行的部分,而不需要程序员手工添加并行指令。
这本质上是一个数据依赖分析问题:如果操作 A 的输出被操作 B 使用,那么 B 必须等 A 完成(数据依赖);如果 A 和 B 完全独立,那么它们可以并行执行。
数据依赖要细分三种:流依赖(先写后读,顺序必须保持)、反依赖(先读后写,换序会读到新值)、输出依赖(两次写同一位置)。只有确认两次迭代之间三种依赖都不存在,循环才能安全并行。别名分析越精确,被误判为"有依赖"的情形就越少,可挖出的并行度就越高。
艾伦与约翰·科克(John Cocke,另一位 IBM 图灵奖得主)合作,发展了系统分析程序数据依赖、判断并行安全性的方法,影响了后来所有并行编译器的设计。
核心贡献四:PTRAN——自动并行化的边界
1980 年代,艾伦在 IBM 沃森研究中心领导 PTRAN(Parallel Translator)项目——她在 IBM 的最后一个大型项目,目标是把普通的顺序 FORTRAN 程序自动翻译成能在多处理器上并行执行的版本。
PTRAN 面对的最硬的骨头是别名:两段代码字面上操作不同的数组,运行时却可能指向同一片内存,此时并行化就是错的。编译器必须在静态分析阶段保守地排除一切"可能冲突"——宁可放弃并行机会,也不能产出错误结果。为此 PTRAN 大幅推进了过程间分析(interprocedural analysis):跨函数追踪数据的定义与使用,判断循环各次迭代之间是否真正独立。"先证明安全、再放手并行"这条纪律,至今仍是自动并行化编译器的基本范式。
同一时期,IBM 还在攻克编译器的另一个瓶颈:寄存器分配。寄存器只有十几个,变量却成百上千——谁驻留寄存器、谁被"挤出"到内存,直接决定程序快慢。科克早在 1971 年就看出这个问题等价于图着色:把每个变量的活跃区间画成一个点,两个区间时间上重叠就连一条边;给这张图着 $k$ 种颜色($k$ 即寄存器数),同色变量即可共用寄存器,着不下时就得选择把谁溢出(spill)到内存。1981 年,格雷戈里·蔡廷(Gregory Chaitin)与同事在 IBM 801 RISC 原型机的 PL.8 编译器中首次实现了这套方案,此后几乎所有主流编译器的寄存器分配器都沿用它。寄存器分配不是艾伦本人的发明,但它生长的土壤——控制流分析、数据流分析、不动点求解——正是她铺好的。
IBM 内的领导力
除了技术贡献,艾伦在 IBM 内部是推动计算机科学研究多元化的重要声音。她积极指导女性工程师和研究员,推动 IBM 重视学术研究的严格性。
1989 年,她成为第一位被命名为 IBM 会士(IBM Fellow)的女性——这是 IBM 对内部研究员的最高荣誉。
2006 年,她获得 ACM 图灵奖,授奖词是"在优化编译器技术的理论与实践方面的开创性贡献,为现代优化编译器与自动并行执行奠定了基础"。她由此成为图灵奖历史上首位女性得主——距离第一届图灵奖颁发,已经过去了整整四十年。
2020 年 8 月 4 日——正是她的 88 岁生日——弗朗西丝·艾伦在纽约斯克内克塔迪去世。
代价:不可见的基础设施
艾伦的工作有一个独特的困境:越成功,越不可见。好的编译器优化是透明的——程序员只管写代码,编译器悄悄地把一切变得更快,没有人注意到。她的贡献直接体现在她没有署名的每一个程序的执行速度上,而这恰恰使得她在公众认知中长期低于实际影响力。
这不只是艾伦一个人的问题,而是整个基础设施工程的困境:操作系统、编译器、网络协议栈的设计者,远不如应用层产品的创造者有名,但前者是后者的地基。
跨域连接
- 图论:把程序切成基本块、以可能的控制转移为边,程序就成了一张有向图。此后"这个变量的值从哪来"变成图上的可达性问题,可以用不动点迭代机械求解。精度的损失点也随之明确:在多条路径汇合处,分析必须把不同路径的信息合并——路径敏感性正是在这一步被交出去的。
- 因果:编译器判断两段代码可否并行,看的是它们之间有没有通过存储位置形成的读写链,也就是有没有因果依赖。耐人寻味的是,判据只看"可能"的别名而不看实际发生,于是指针别名分析的保守程度直接决定了并行度。推论很实用:给编译器更多别名信息就能立刻放宽判断,算法一行不用改。
- 气候模拟:数值模式的可行分辨率受总浮点吞吐限制,而吞吐一半来自硬件,另一半来自编译器能否把嵌套循环向量化并保住缓存局部性。这带来一个容易被忽略的推论:同一台机器上,模式能算多细会随编译器与数学库的改进而提高——这是纯软件带来的科学能力增量。
- 社会分层:一项工作的社会可见度,取决于它的产出能否被单独归因。编译器优化的收益分散在所有下游程序里,无法归给任何一次署名,于是越成功越不可见。推论也跟着走:这类岗位的声望与报酬更依赖机构内部评价而非外部市场,因而对机构政策异常敏感。
- GPU 与并行计算:今天写下一行并行指令就能用上数千个核,前提是依赖分析已经替你确认了这些迭代之间没有冲突。数据依赖这套判据在她手里被系统化,此后自动向量化、循环分块与线程化都长在同一根理论上;不满足依赖条件的循环,堆多少核都提不上去。
参考文献
- Allen, F. E. Control Flow Analysis. ACM SIGPLAN Notices 5(7) (1970): 1–19. (控制流图分析的奠基性论文)
- Allen, F. E. & Cocke, J. A Catalogue of Optimizing Transformations. In Rustin, R. (ed.), Design and Optimization of Compilers. Prentice Hall (1972). (编译器优化方法的系统分类)
- Allen, F. E. & Cocke, J. A Program Data Flow Analysis Procedure. Communications of the ACM 19(3), 1976. (数据流分析过程的系统化)
- Allen, F. E. The History of Language Processor Technology in IBM. IBM Journal of Research and Development 25(5) (1981): 535–548.
- Chaitin, G. J., Auslander, M. A., Chandra, A. K., Cocke, J., Hopkins, M. E. & Markstein, P. W. Register Allocation via Coloring. Computer Languages 6(1), 1981. (图着色寄存器分配的首次实现)
- Aho, A. V., Lam, M. S., Sethi, R. & Ullman, J. D. Compilers: Principles, Techniques, and Tools. 2nd ed. Addison-Wesley (2006). ("龙书",标准编译器教材,建立在艾伦等人工作的基础上)