Rust 编译的哪个部分是瓶颈?

Rust 编译时间是网上讨论的热门话题。多数人不在意编译 Rust 程序时具体哪些耗时,只希望整体更快。有时人们会指责特定因素如 LLVM 或借用检查器。本文通过一个有趣实验收集数据,试图在各种情况下找出罪魁祸首。

  • 几个月前开始思考,为查看编译细分,在 Rust 编译器基准测试套件中添加新可视化方式,能显示编译三个高级“部分”的时间比例:前端(词法分析、解析等)、后端(代码生成,目前用 LLVM)、链接器(最终产物链接,目前用默认 Linux bfd链接器)。该可视化有用,可明确优化重点及编译瓶颈。数据从编译器自分析机制记录的查询轨迹中提取。
  • 进行“前 100 个 crate 实验”,下载crates.io上最流行的 100 个 crate 作为基准,用近期nightly编译器编译并收集自分析数据,创建多个图表。每个基准在 12 种不同配置下执行,包括三种配置文件(CheckDebugOpt)和四种场景(FullIncrFullIncrPatchedIncrUnchanged),只测量叶子 crate,不包括依赖项编译。
  • ripgrep二进制 crate 为例,不同配置下编译时间分布不同。调试模式下全量构建时,大部分时间等待 LLVM,前端单线程,后端最多 16 线程并行;增量调试构建时,链接器成为更大瓶颈,借用检查消失;优化模式下增量构建时,后端占比变大;仅运行cargo check时,无链接器部分,前端耗时最多。对其他二进制 crate(hyperfinedust)平均后,后端耗时更多,因其使用lto = true
  • 测量regex-automata库 crate,调试模式下全量构建时前端有时是“坏人”,有新的元数据部分;增量小改时前端占比更大。对约 90 个库平均后,前端在库编译中占比更大。可见瓶颈在前端或后端取决于编译内容。
  • 思考二进制和库编译的常见情况,大多数项目依赖的 crate 是库,但通常编译不频繁,而开发时主要卡在编译二进制产物。作者认为交互式编辑-构建-运行周期是 Rust 开发的最大瓶颈,后端和链接器可改进最多。编辑帖子后意识到二进制与库的区分有误导性,重要的是是否生成可链接产物。
  • 对于这些信息,目前rustc自身不计算编译细分,若 Cargo 能显示编译瓶颈总结并引导优化就好了。最后总结,编译瓶颈取决于具体情况,应全面提高编译器性能,若不想思考可使用cargo-wizard工具的fast-compile配置文件。若想查看测量数据或自己运行实验,可在相关仓库找到脚本。

主要观点:Rust 编译时间的瓶颈取决于编译内容,前端和后端都可能是瓶颈,且开发时二进制编译更重要,希望 Cargo 能提供编译瓶颈总结及优化引导。

关键信息:通过实验展示不同 crate 在各种配置下的编译时间细分情况,包括前端、后端和链接器的耗时比例,以及不同类型 crate(二进制和库)的差异。

重要细节:添加新可视化方式到 Rust 编译器基准测试套件,实验中对 100 个 crate 及多个库进行测量,以ripgrepregex-automata为例详细说明不同配置下的编译情况,讨论二进制和库编译的常见情况及对未来的期望等。

阅读 17
0 条评论