Emitting metadata early makes building/checking Rust up to twice as fast
Emitting metadata early makes building/checking Rust up to twice as fast
提前生成元数据使 Rust 的构建/检查速度提升最高可达两倍
headstart Start dependent crates before their dependencies finish type-checking. Every crate waits for the crates it depends on to be fully checked, function bodies included, before it starts. It doesn’t need those bodies to type-check itself. It compiles against the dependency’s interface, the metadata in its .rmeta file.
headstart 允许在依赖项完成类型检查之前启动相关 crate 的构建。通常,每个 crate 在启动前都要等待其依赖项完全检查完毕(包括函数体)。但实际上,它并不需要这些函数体来进行自身的类型检查,它只需要依赖项的接口,即 .rmeta 文件中的元数据。
Headstart makes rustc write an early metadata file as soon as the interface is checked, and makes cargo start dependents on it. Each crate’s bodies are then checked while the crates downstream are already compiling.
Headstart 让 rustc 在接口检查完成后立即写入一个早期元数据文件,并让 cargo 基于此启动下游 crate 的构建。这样,当下游 crate 正在编译时,当前 crate 的函数体检查工作仍在继续。
cargo check: dependents run to completion on early metadata. cargo build: dependents do all their analysis on early metadata, then wait for the dependency’s full metadata before generating code. While they wait, they give their job slot back.
cargo check:下游 crate 可以仅凭早期元数据运行至完成。cargo build:下游 crate 使用早期元数据完成所有分析,然后在生成代码前等待依赖项的完整元数据。在等待期间,它们会释放占用的构建槽位(job slot)。
If a body has an error, the build still fails with that error, with the same diagnostics and exit status as today; only progress lines and the cross-crate order of JSON messages can differ. Cargo reports a crate’s output only once all its dependencies have finished cleanly, and drops it if one fails. The costs are work downstream that gets thrown away, errors reported slightly later, and more memory in use at once (see docs/design.md).
如果函数体中存在错误,构建仍会因该错误而失败,并提供与当前版本相同的诊断信息和退出状态;仅进度行和跨 crate 的 JSON 消息顺序可能有所不同。Cargo 仅在所有依赖项顺利完成后才会报告 crate 的输出,如果其中一个失败,则会丢弃该输出。其代价是下游可能产生被丢弃的无效工作、错误报告略有延迟,以及同时占用更多内存(详见 docs/design.md)。
Pieces rustc, -Zearly-metadata (6 patches): a new analysis_interfaces query splits analysis into item interfaces and function bodies; the driver writes .early-rmeta between the two; crate loading accepts early metadata, and swaps in full metadata before code generation, waiting for it if necessary on a lock its producer holds until it’s written.
组件 rustc, -Zearly-metadata(6 个补丁):引入了一个新的 analysis_interfaces 查询,将分析过程拆分为“项接口”和“函数体”两部分;驱动程序在这两者之间写入 .early-rmeta;crate 加载器接受早期元数据,并在代码生成前将其替换为完整元数据,必要时会通过生产者持有的锁进行等待,直到完整元数据写入完成。
cargo, -Zheadstart (3 patches): passes -Zearly-metadata to every compile; starts dependents on the early-metadata notification, in both check and build; gives a paused compilation’s job slot to other work; reports a crate’s output only when its dependencies succeeded. The patches are a commit series, each with a commit message and tests, meant to become upstream pull requests: see patches/README.md.
cargo, -Zheadstart(3 个补丁):将 -Zearly-metadata 传递给每次编译;在 check 和 build 模式下,根据早期元数据通知启动下游任务;将暂停编译的任务槽位让给其他工作;仅在依赖项成功时才报告 crate 的输出。这些补丁是一系列提交,每个都有提交信息和测试,旨在成为上游的 Pull Request:详见 patches/README.md。
On rustc’s default front end, headstart makes clean builds of 13 real projects (rust-analyzer, zed, bevy, lemmy, polars and others) up to 54% faster for cargo check, and up to 42% for cargo build. None is slower. With the parallel front end (-Zthreads=8), which covers some of the same ground, it adds up to 25%. Those are 16-core numbers.
在 rustc 的默认前端上,headstart 使 13 个真实项目(如 rust-analyzer、zed、bevy、lemmy、polars 等)的全新构建在 cargo check 中速度提升最高达 54%,在 cargo build 中提升最高达 42%。没有任何项目变慢。在使用并行前端(-Zthreads=8,涵盖了部分相同优化)的情况下,它还能额外提升最高 25% 的速度。以上数据基于 16 核机器。
The gain comes from cores the build would leave idle, so it shrinks on smaller machines. On 4 cores, rust-analyzer’s check is 24% faster and its build 13–15%, codex-rs’s check 14%, and wide builds come out even.
性能提升源于利用了原本处于空闲状态的 CPU 核心,因此在较小的机器上提升幅度会减小。在 4 核机器上,rust-analyzer 的 check 速度提升 24%,build 提升 13–15%,codex-rs 的 check 提升 14%,而宽构建(wide builds)则持平。
A clean cargo build of codex-rs on 16 cores, recorded with cratebank. Without headstart, the workspace’s own crates compile one after another while the machine sits mostly idle; with it, each starts on the early metadata of the one before, and the build is 37% faster.
在 16 核机器上对 codex-rs 进行全新 cargo build 的记录显示:没有 headstart 时,工作空间内的 crate 一个接一个地编译,机器大部分时间处于空闲状态;使用 headstart 后,每个 crate 都能基于前一个 crate 的早期元数据启动,构建速度提升了 37%。
The same two builds, with time across and dependency depth down: each unit is drawn under the dependency that released it, coloured by compiler phase. Without headstart the workspace crates form a long staircase; with it they overlap. More in docs/results.md.
对比这两次构建,横轴为时间,纵轴为依赖深度:每个单元绘制在其释放它的依赖项下方,并按编译器阶段着色。没有 headstart 时,工作空间内的 crate 形成长长的阶梯状;使用后,它们则相互重叠。更多信息请见 docs/results.md。
How it works, what early metadata leaves out, and the risks: docs/design.md. Measurements: docs/results.md. Whether it’s ready to bring to the compiler and cargo teams: docs/readiness.md.
工作原理、早期元数据遗漏的内容及风险:docs/design.md。测量数据:docs/results.md。是否准备好提交给编译器和 Cargo 团队:docs/readiness.md。
Try it:
scripts/setup.sh # check out rustc + cargo, apply the patches, build both
尝试使用:
scripts/setup.sh # 检出 rustc 和 cargo,应用补丁,并构建两者
Then, in any Rust project:
RUSTC=/path/to/headstart/rustc/build/host/stage1/bin/rustc \ /path/to/headstart/cargo/target/release/cargo check -Zheadstart # or build
然后在任何 Rust 项目中:
RUSTC=/path/to/headstart/rustc/build/host/stage1/bin/rustc \ /path/to/headstart/cargo/target/release/cargo check -Zheadstart # 或 build
CARGO_UNSTABLE_HEADSTART=true turns it on too, as does [unstable] headstart = true in .cargo/config.toml. Without it, the patched cargo behaves like upstream, so the same binaries give a fair baseline.
CARGO_UNSTABLE_HEADSTART=true 也可以开启该功能,在 .cargo/config.toml 中设置 [unstable] headstart = true 同样有效。如果不开启,打过补丁的 cargo 表现与上游版本一致,因此相同的二进制文件提供了公平的基准对比。