Reduce Compilation Duration
Recipe | Crates | Categories |
---|---|---|
Use Dynamic Linking | ||
Incremental Compilation | ||
Measure Build Times | ||
Optimize Compilation Levels | ||
Measure Build Times |
Rust compile times can be long. Reducing Rust compilation duration involves several strategies, targeting both the compiler and project structure.
Methods | Description |
---|---|
Incremental Compilation | Leverage Cargo's caching, be mindful of changes that invalidate the cache. |
Dependency Management | Use cargo tree to analyze dependencies. cargo-bloat ⮳ can help you identify large dependencies contributing to compile times. |
Compiler Flags | Experiment with compiler flags, but be careful and measure the impact. |
Build Profiles | Optimize release builds with appropriate flags in config.toml . |
Link-Time Optimization (LTO) | Controlled via Cargo.toml and config.toml . |
Profiling | cargo flamegraph, perf (system profiler) |
Code Structure | Avoid excessive monomorphization, consider techniques like dynamic dispatch where applicable. |
Measure Build Times
time cargo build
cargo build --timings
You may also use hyperfine
. See Benchmarking.
Incremental Compilation
Incremental compilation in Rust is built into Cargo and rustc
, and generally "just works" automatically. It reuses previously compiled code, significantly speeding up subsequent builds after changes.
Keeping in mind how the incremental compiler works is key to maximizing its benefits. Changes to dependencies or function signatures can invalidate the cache. Strategies to minimizing cache invalidation include:
- Keeping dependencies stable,
- Structuring code to minimize changes that trigger recompilation (see below),
- Avoid actions that invalidate the cache, such as changing dependencies (modifying
Cargo.toml
) or build scripts unnecessarily. - Being mindful of how generics and macros can affect recompilation.
From-scratch builds with incremental compilation enabled adds about 15–20% overhead compared to disabled. The initial build needs to write out more intermediate state in order for later incremental builds to take advantage of it. In a CI situation, it would be unusual for there to be a later incremental build within the same job. Thus consider disabling incremental compilation in that context.However, CI workflows that cache the target directory across runs may be benefiting from incremental compilation.
Project Structure and Dependencies
- Reduce the number of dependencies. Analyze your
Cargo.toml
and eliminate unused or redundant dependencies. Consider alternatives if a dependency is excessively large or slow to compile. - For multi-crate projects, use a workspace to share dependencies and enable workspace-level optimizations. This can reduce redundant compilation.
- Use feature flags to conditionally compile your code and dependencies. This allows you to exclude unnecessary code during development, reducing compilation time.
- For large, infrequently changing dependencies, consider using precompiled versions if available.
- Organizing your code into smaller, independent modules and crates can improve incremental compilation by reducing the scope of changes.
cargo tree
is a useful tool for dependency analysis.
Compiler-Level Optimizations
- Use the
dev
profile for development, which prioritizes fast compilation over optimizations. Switch to therelease
profile only for final builds. - Increasing the number of codegen units (
rustc
flag:-C codegen-units=N
) allows the compiler to parallelize code generation. Experiment to find the optimal value; too many can hinder Link-Time Optimization (LTO). - Tune Link-Time Optimization (LTO). While LTO can improve runtime performance, it increases compile time, especially "fat" LTO.
Consider using "thin" LTO (
-C lto=thin
) for a faster, though less aggressive, approach. For debug builds, disable LTO entirely (-C lto=no
). - Profile-Guided Optimization (PGO): PGO can improve runtime performance but requires additional compilation and profiling steps, thus increasing overall build time. Use PGO only for release builds where runtime performance is critical, and not for general development.
- Using
sccache
can dramatically speed up compilation by caching compiled objects. It's particularly effective when recompiling similar code across multiple projects or branches.
Optimize Compilation Levels
In config.toml
# Enable a small amount of optimization in debug mode
[profile.dev]
opt-level = 1
# Enable high optimizations for dependencies, but not for our code:
[profile.dev.package."*"]
opt-level = 3
Use Dynamic Linking
cargo install cargo-add-dynamic
cargo add-dynamic polars --features csv-file,lazy,list,describe,rows,fmt,strings,temporal
cargo build
See also: Speeding up incremental Rust compilation with dylibs⮳.
Build Machine Hardware Considerations
Build your code with the right machine:
- A multi-core CPU significantly speeds up compilation, especially when using multiple codegen units.
- Sufficient RAM is essential, especially when using LTO or compiling large projects.
- A fast SSD can greatly reduce I/O bottlenecks during compilation.
Consider using remote build servers or a separate build machine for large projects.
References
- Eight solutions for troubleshooting your Rust build times⮳.
- How I improved my Rust compile times by seventy-five percent⮳.
- Rust compilation time⮳.
Related Topics
- Benchmarking.
- Build Utils.
- Development Tools.
- Development Tools: Build Utils.
- Development Tools: Cargo Plugins.
- Development Tools: Profiling.
- Faster Linking.
- Performance.