Rust build times
Compile time is a measurement problem first. Profile before changing anything, apply one change at a time, and keep only measured wins.
Contract
| Field |
Bound contract |
| Trigger |
A Rust build is slow and the user wants it measured and reduced: cargo build --timings, sccache, the Cranelift backend, workspace splitting, LTO tuning, or a faster linker. |
| Authority |
Reversible local. Writes only the project's Cargo.toml and .cargo/config.toml; rollback is version control. Toolchain component and system package installs are reported as commands for the user, not executed. No remote mutation. |
| Side effect |
Edits project build configuration and emits a measurement report. |
| Done |
A baseline cargo build --timings report exists, each applied change is measured against it, and only measured wins remain in the configuration. |
Inputs
- Build command (required): the slow invocation, such as
cargo build, cargo test, or cargo build --release.
- Workspace layout (required if not inferrable): single crate or workspace, member count, and dependency graph.
- Toolchain (required): stable or nightly; the Cranelift backend needs nightly.
- Constraints (optional): CI environment, cache storage (local disk or S3), and whether runtime performance may regress.
Procedure
Measure the baseline. Run cargo build --timings (add --release when release is the slow path) and read the HTML report for long serial chains, crates that dominate the timeline, and proc-macro crates blocking downstream work. Run cargo llvm-lines --release to find monomorphization-heavy functions. Done when: the top offenders are named.
Add sccache for repeated builds. sccache wraps rustc and caches compiled artifacts locally or in S3.
export RUSTC_WRAPPER=sccache
sccache --show-stats
# .cargo/config.toml
[build]
rustc-wrapper = "sccache"
For CI, the S3 backend is configured through SCCACHE_BUCKET and SCCACHE_REGION. Done when: sccache --show-stats reports hits on a rebuild.
- Select the Cranelift backend for dev builds when nightly is allowed. Cranelift compiles faster than LLVM and produces slower code, so it belongs in the dev profile only. Done when: the dev profile uses Cranelift and release stays on LLVM.
# .cargo/config.toml (nightly only)
[unstable]
codegen-backend = true
[profile.dev]
codegen-backend = "cranelift"
The component is rustc-codegen-cranelift-preview on the nightly toolchain; report the rustup component add command for the user to run.
Split the workspace for parallelism. One large crate compiles serially; independent crates compile in parallel. Break circular dependencies first, move proc macros into their own crate, and isolate frequently changed code so edits invalidate less. Inspect the graph with cargo tree. Done when: the timing report shows parallel compilation where a serial chain ran before.
Tune LTO per profile. LTO trades link time for runtime performance: false for dev, "thin" for most release builds, true (fat) only when runtime performance is measured to matter. codegen-units = 1 maximizes optimization and serializes codegen. Done when: each profile carries the LTO setting its purpose needs.
[profile.release]
lto = "thin"
[profile.dev]
codegen-units = 16
- Switch to a faster linker. The link step often dominates large projects. mold is the fastest option on Linux; lld is the portable fast option. Both are selected through the clang driver. Done when: the configured linker appears in the build log and link time drops.
# .cargo/config.toml
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"] # or =lld
Report the system package install command for the user rather than running it.
Apply the smaller wins. debug = 1 in the dev profile emits line tables only; split-debuginfo = "unpacked" shrinks linker input on platforms that support it; CARGO_INCREMENTAL=0 can win on clean CI builds. Done when: each candidate is measured, not assumed.
Re-measure after every change. Compare each timing report against the baseline and keep only changes that measurably help this workload. Done when: the final configuration is the measured fastest acceptable set.
Failure and recovery
| Failure class |
Behavior |
--timings produces no report |
Requires a recent Cargo; record the toolchain version and fall back to per-crate timing from cargo build -vv output. |
| sccache reports no hits |
Check RUSTC_WRAPPER or rustc-wrapper is set in the right config file and that the cache directory is writable. |
| Cranelift rejects a crate |
Some intrinsics and SIMD paths are unsupported; keep that crate or profile on LLVM. |
| Workspace split hits a dependency cycle |
Extract the shared types into a common crate both sides depend on. |
| mold or lld not installed |
Report the install command; do not run system package managers. |
| A change regresses the build |
Revert it; the baseline report is the arbiter. |
| Partial result |
Configuration edits already made remain; the report names the unmeasured or reverted changes. Rollback is version control. |
Output
- A measurement report: baseline timing, the named bottleneck crates, and per-change before/after numbers.
- The final Cargo.toml and .cargo/config.toml edits that survived measurement.
1---2name: rust-build-times3description: Use when profiling slow Rust builds with cargo --timings, configuring sccache, selecting the Cranelift dev backend, splitting a workspace for parallelism, or tuning LTO and the linker.4---56# Rust build times78Compile time is a measurement problem first. Profile before changing anything, apply one change at a time, and keep only measured wins.910## Contract1112| Field | Bound contract |13|---|---|14| Trigger | A Rust build is slow and the user wants it measured and reduced: `cargo build --timings`, sccache, the Cranelift backend, workspace splitting, LTO tuning, or a faster linker. |15| Authority | Reversible local. Writes only the project's Cargo.toml and .cargo/config.toml; rollback is version control. Toolchain component and system package installs are reported as commands for the user, not executed. No remote mutation. |16| Side effect | Edits project build configuration and emits a measurement report. |17| Done | A baseline `cargo build --timings` report exists, each applied change is measured against it, and only measured wins remain in the configuration. |1819## Inputs20211. **Build command** (required): the slow invocation, such as `cargo build`, `cargo test`, or `cargo build --release`.222. **Workspace layout** (required if not inferrable): single crate or workspace, member count, and dependency graph.233. **Toolchain** (required): stable or nightly; the Cranelift backend needs nightly.244. **Constraints** (optional): CI environment, cache storage (local disk or S3), and whether runtime performance may regress.2526## Procedure27281. **Measure the baseline.** Run `cargo build --timings` (add `--release` when release is the slow path) and read the HTML report for long serial chains, crates that dominate the timeline, and proc-macro crates blocking downstream work. Run `cargo llvm-lines --release` to find monomorphization-heavy functions. Done when: the top offenders are named.29302. **Add sccache for repeated builds.** sccache wraps rustc and caches compiled artifacts locally or in S3.3132```bash33export RUSTC_WRAPPER=sccache34sccache --show-stats35```3637```toml38# .cargo/config.toml39[build]40rustc-wrapper = "sccache"41```4243For CI, the S3 backend is configured through `SCCACHE_BUCKET` and `SCCACHE_REGION`. Done when: `sccache --show-stats` reports hits on a rebuild.44453. **Select the Cranelift backend for dev builds when nightly is allowed.** Cranelift compiles faster than LLVM and produces slower code, so it belongs in the dev profile only. Done when: the dev profile uses Cranelift and release stays on LLVM.4647```toml48# .cargo/config.toml (nightly only)49[unstable]50codegen-backend = true5152[profile.dev]53codegen-backend = "cranelift"54```5556The component is `rustc-codegen-cranelift-preview` on the nightly toolchain; report the `rustup component add` command for the user to run.57584. **Split the workspace for parallelism.** One large crate compiles serially; independent crates compile in parallel. Break circular dependencies first, move proc macros into their own crate, and isolate frequently changed code so edits invalidate less. Inspect the graph with `cargo tree`. Done when: the timing report shows parallel compilation where a serial chain ran before.59605. **Tune LTO per profile.** LTO trades link time for runtime performance: `false` for dev, `"thin"` for most release builds, `true` (fat) only when runtime performance is measured to matter. `codegen-units = 1` maximizes optimization and serializes codegen. Done when: each profile carries the LTO setting its purpose needs.6162```toml63[profile.release]64lto = "thin"6566[profile.dev]67codegen-units = 1668```69706. **Switch to a faster linker.** The link step often dominates large projects. mold is the fastest option on Linux; lld is the portable fast option. Both are selected through the clang driver. Done when: the configured linker appears in the build log and link time drops.7172```toml73# .cargo/config.toml74[target.x86_64-unknown-linux-gnu]75linker = "clang"76rustflags = ["-C", "link-arg=-fuse-ld=mold"] # or =lld77```7879Report the system package install command for the user rather than running it.80817. **Apply the smaller wins.** `debug = 1` in the dev profile emits line tables only; `split-debuginfo = "unpacked"` shrinks linker input on platforms that support it; `CARGO_INCREMENTAL=0` can win on clean CI builds. Done when: each candidate is measured, not assumed.82838. **Re-measure after every change.** Compare each timing report against the baseline and keep only changes that measurably help this workload. Done when: the final configuration is the measured fastest acceptable set.8485## Failure and recovery8687| Failure class | Behavior |88|---|---|89| `--timings` produces no report | Requires a recent Cargo; record the toolchain version and fall back to per-crate timing from `cargo build -vv` output. |90| sccache reports no hits | Check `RUSTC_WRAPPER` or `rustc-wrapper` is set in the right config file and that the cache directory is writable. |91| Cranelift rejects a crate | Some intrinsics and SIMD paths are unsupported; keep that crate or profile on LLVM. |92| Workspace split hits a dependency cycle | Extract the shared types into a common crate both sides depend on. |93| mold or lld not installed | Report the install command; do not run system package managers. |94| A change regresses the build | Revert it; the baseline report is the arbiter. |95| Partial result | Configuration edits already made remain; the report names the unmeasured or reverted changes. Rollback is version control. |9697## Output98991. A measurement report: baseline timing, the named bottleneck crates, and per-change before/after numbers.1002. The final Cargo.toml and .cargo/config.toml edits that survived measurement.