Reduce Compilation Duration

Rust compile times can be long. Reducing Rust compilation duration involves several strategies, targeting both the compiler and project structure. Here's a breakdown:

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.
  • Avoid Unnecessary Recompilation: Be mindful of changes that trigger recompilation. For example, changing build scripts or modifying Cargo.toml can invalidate the cache.
  • Use feature flags to conditionally compile code. This allows you to exclude unnecessary code during development, reducing compilation time.
  • Precompiled Dependencies: For large, infrequently changing dependencies, consider using precompiled versions if available.
  • Code Organization: Organizing your code into smaller, independent modules can improve incremental compilation by reducing the scope of changes.

Compiler-Level Optimizations

  • Build Profiles: Use the dev profile for development, which prioritizes fast compilation over optimizations. Switch to the release profile only for final builds.
  • Ensure incremental compilation is enabled (the default). It reuses previously compiled code, significantly speeding up subsequent builds after changes. Avoid actions that invalidate the cache, such as changing dependencies or build scripts unnecessarily.
  • 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.
  • Ccache: Using ccache can dramatically speed up compilation by caching compiled objects. It's particularly effective when recompiling similar code across multiple projects or branches.

Build Machine Hardware Considerations

  • CPU Cores: A multi-core CPU significantly speeds up compilation, especially when using multiple codegen units.
  • RAM: Sufficient RAM is essential, especially when using LTO or compiling large projects.
  • SSD: A fast SSD can greatly reduce I/O bottlenecks during compilation.

Tools

  • cargo-bloat can help you identify large dependencies contributing to compile times.
  • cargo-graph can visualize your dependency graph, making it easier to identify potential issues.

Profiling: cargo flamegraph, perf (system profiler) Code Optimization: (Often done without specific crates, focusing on algorithmic efficiency, data structures, and avoiding unnecessary allocations/copies) Dependency Management: (Minimize dependencies, use cargo tree to analyze) Link-Time Optimization (LTO): (Controlled via Cargo.toml) Incremental Compilation: (Leverage Cargo's caching, be mindful of changes that invalidate the cache) Build Profiles: (Optimize for release builds with appropriate flags in Cargo.toml) Compiler Flags: (Experiment with compiler flags, but be careful and measure improvements) Code Generation: (Avoid excessive monomorphization, consider techniques like dynamic dispatch where applicable)

Measure Build Times

cargo-website cat-compilers

time cargo build
cargo build --timings

Optimize Compilation Levels

cargo-website cat-compilers

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-add-dynamic cargo-add-dynamic-crates.io cargo-add-dynamic-github cargo-add-dynamic-lib.rs cat-development-tools cat-compilers

cargo install cargo-add-dynamic
cargo add-dynamic polars --features csv-file,lazy,list,describe,rows,fmt,strings,temporal
cargo build

Speeding up incremental Rust compilation with dylibs⮳.

Compile Incrementally

cat-compilers

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 extremely unusual for there to be a later incremental build within the same job. The jobs are not making changes to source code and rebuilding. However, workflows that cache the target directory across runs might be benefiting from incremental compilation.

References

Related Topics

  • Benchmarking.
  • Build Utils.
  • Development Tools.
  • Development Tools: Build Utils.
  • Development Tools: Cargo Plugins.
  • Development Tools: Profiling.
  • Faster Linking.
  • Performance.