我上一篇关于 Rust 编译器性能的文章是两个月前发布的,此后发生了不少事情。
总体进展
2026-07-29 至 2026-09-28 期间的测量数据可在 perf.rust-lang.org 查看。
平均挂钟时间(wall-time)缩短了 4.57%,在短短两个月内这是相当显著的改进。在 629 项基准测量中,555 项得到改善,只有 74 项出现回退。一些基准测试实现了两位数百分比的缩短。用技术术语来说,这个结果就是“一片绿海”(a sea of green)。
rustdoc
在上一篇文章中,我提到 Noah Lev 在 rustdoc 上取得了巨大的速度提升。他最近撰文详细解释了具体的实现方法。这是一篇有趣且令人满足的读物。
Clippy
#159642:在这个 PR 中,Jakub Beránek 为 Clippy 启用了 PGO(基于配置的优化),使大多数 Clippy 基准测试的挂钟时间得到改善,最佳情况下提升了 18%!
LLVM 更新
#158734:在这个 PR 中,Nikita Popov 将编译器使用的 LLVM 版本升级到 LLVM 23。正如我们升级 LLVM 时经常出现的情况,我们看到了一些不错的提速。所有基准测试的平均挂钟时间缩短了 1.2%,听起来可能不多,但对于单个 PR 来说已经非常可观了。LLVM 团队干得漂亮!
新的借用检查器
新的借用检查器 Polonius Alpha(与 Napoleon Dynamite 没有任何关系)已在 Nightly 上启用。它比现有的借用检查器更精确,可以接受一些旧借用检查器会拒绝的合法程序。它确实比旧借用检查器做更多的工作,足以在少数情况下对编译时间产生可测量的影响,包括流行的 serde crate。幸运的是,Jack Huey 一直在处理这个问题。
#161938:在这个 PR 中,Jack 将一些活跃性(liveness)计算改为惰性执行,使 serde 的指令数减少了 3-5%,其他一些基准测试的减少幅度不到 1%。
#163027:在这个 PR 中,Jack 调整了一个数据结构并微调了一些内联,在众多基准测试中实现了普遍低于 1% 的指令数减少。
要消除 Polonius Alpha 剩余的性能回退还有很多工作要做,但值得注意的是,“一片绿海”表明这些回退已被近期许多其他改进所淹没。
新的 trait solver
新的 trait solver,Penelope Hammertime(编者按:是这个名字吗?)也已在 Nightly 上启用。
正如我所说,发生了很多事情。
与新的借用检查器一样,新的 trait solver 在少数情况下也更慢。Jana Dönszelmann 撰写了一篇详细的文章,介绍了为改进这个新求解器性能所做的努力。
Jana 的文章足够详细,我就不再赘述关于新求解器正在进行的大量工作,但我会顺带提及我提交的 PR:#160479、#160605、#160801、#160892、#161077 和 #161211。其中一些 PR 大幅缩短了某些离群(outlier)crate 的编译时间:这里有 50%,那里有 25%,还有 15%,在一个压力测试中甚至更多。而且并不只有我在这方面取得了进展……去读读 Jana 的文章吧。
xmakro
新贡献者 xmakro 继续保持一系列出色改进的势头。
#157281:在这个 PR 中,xmakro 优化了构建特化图(specialization graph)时的 impl 处理。这使得所有基准测试的平均周期数减少了 1.58%,对于单个 PR 来说非常巨大。
#158059:在这个 PR 中,xmakro 优化了增量编译数据加载的一个方面,在多个基准测试中减少了指令数,最佳情况下减少了 6%。
#160473:在这个 PR 中,xmakro 在一条热门的 obligations 处理路径中避免了一些内存分配,在众多基准测试中减少了指令数,最佳情况下减少了 2%。
#160268:在这个 PR 中,xmakro 通过将旧/新 trait solver 的选择代码改为使用静态分发而非动态分发,避免了大量内存分配。这在许多基准测试中带来了普遍低于 1% 的指令数减少。这条热门分配路径已经在性能剖析中出现了一段时间,我之前曾在 #155714 中尝试过完全相同的想法,但我在几个基准测试上遇到了回退,可能是由于 #[inline] 属性放置位置的选择略有不同。很高兴看到这个明显的低效问题被修复。
数据流分析
#160193:在这个 PR 中,我改变了编译器中数据流分析所使用的 CFG 遍历算法。这些分析会迭代到不动点(fixpoint),遍历算法会影响到达不动点的速度。对于大多数代码,新算法没有影响,但 cranelift-codegen crate 有一个超过 18,000 个基本块的巨大函数。旧算法需要 150 万次调用 apply_effects_in_block 才能让借用检查器所使用的 EverInitializedPlaces 分析达到不动点;新算法只需要 90,000 次。这使该 crate 的 check 构建获得了约 30% 的巨大挂钟时间缩短。
#160033:在这个 PR 中,我再次提高了 EverInitializedPlaces 的效率,这次是通过不跟踪投影(projections)的不必要数据。这使 match-stress 基准测试的指令数减少了 17%,其他几个基准测试的减少幅度不到 1%。
LLM
它们在某些类型的分析上已经变得非常擅长。我仍然自己编写所有代码和文字,因为 (a) 这至关重要,(b) 项目政策有此要求,但在本文提到的几个 PR 中,我获得了 LLM 分析方面的有用帮助。
总之,关于这个话题就说这么多。
杂项
#160535:在这个 PR 中,Chris Denton 增加了编译器使用的默认栈大小,从而可以移除 ensure_sufficient_stack——一种手动栈扩展机制,散布在容易出现高强度递归的地方。这个 PR 引起了很多讨论,因为如何最好地处理栈耗尽问题可能很难决断。但性能效果是明显的,在许多基准测试中减少了指令数,最佳情况下减少了近 3%。
#160506:该项目使用大量“rollup” PR,即多个 PR 合并在一起。这是因为我们没有足够的 CI 容量来逐个合并每个 PR。通常,影响性能的 PR 会单独合并,以便我们能清楚地测量它们的效果。有史以来第一次,曾有一段时间等待合并队列中的性能改进 PR 多到 Jonathan Brouwer 创建了一个包含 10 个性能改进 PR 的 rollup 来保持进展!这是一个甜蜜的烦恼。后来我们还有包含四个性能改进 PR 的 #162859。(你不必担心意外效果混入其中,因为我们有能力在合并后对各个 PR 运行性能基准测试套件,以确保每个 PR 都达到了预期的性能效果。)
#162747:在这个 PR 中,我对将 AST 降级为 HIR 的代码做了一些小改进。这是一次清理,原本预计不会影响性能,但它在众多基准测试中减少了指令数,最佳情况下减少了 1.5%。有时候你会有好运。
工作状态
明天我将开始在 Hexcat 从事编译器性能优化项目目标的工作。这令人兴奋!非常感谢 Mara Bos、Predrag Gruevski 以及所有帮助促成此事的人。
编者后记
新求解器的名字不是 Penelope Hammertime;那是个玩笑。
作者后记
它的真名是 Pineapple Häagen-Dazs。