Skip to main content

How-to compile rust faster

Summary: #

This blog post goes throw multiple ways that you can optimize your system to compile rust faster.

Running Rust Crab

Compile rust faster #

A great analogy for Rust is that of a critical but helpful spouse. Imagine you’re heading to a job interview and ask your spouse how you look. Let’s compare two types of spouses: the lenient language spouse and the strict Rust spouse.

The lenient language spouse says, “You look great! Good luck!” You feel confident but might miss important details if you’re not an expert.

The Rust spouse, however, won’t let you leave without thorough checks. “It’s too hot; change your suit,” they say. You change. “Your suit doesn’t match your socks; change to grey socks.” You comply. “It’s windy; use hair gel.” You do. “The parking lot requires $2.50 in change; find some.” You find the change.

This process repeats many times, and though it’s frustrating, you know your Rust spouse is right. Finally, after many adjustments, your Rust spouse says, “Off you go.” You leave, a bit frustrated, but soon realize you look great, your hair is perfect despite the wind, and you have the exact change for parking. - Daye Macleod

Let’s explore how to compile our critical spouse faster :)

Faster disk speed with Tmpfs #

Your filesystem might be slowing you down. If you are running Linux, MacOS or BSD a quick solution would be to create a RAM-powered directory, saving the files in memory instead of on the disk. Linux, MacOS and BSD both take advantage of in memory storage to create the /tmp file system every time your computer turns on. By utilizing Tmpfs we get much faster read and write operations, saving a ton in compilation.

Utilize your operating system #

$ cd /tmp/ 
$ git clone my_rust_repo
$ cd my_rust_repo/ && cargo build --release  

or

Create a tmpfs directory #

mkdir -p /mnt/fast #here we define where our new directory will be
mount -t tmpfs -o size=666M tmpfs /mnt/fast # after we have created the folder, lets mount a tmpfs disk, select the size of the disk, in this case 666Megabytes, 
cd /mnt/fast # go into your new tmpfs directory and compile your rust code!

Note: All tmpfs directories are Temporary so rember to backup and push your commits.

Read more about Tmpfs: https://en.wikipedia.org/wiki/Tmpfs

Change your Linker #

When you’re compiling Rust code, a large percentage of resources are spent on linking. Which puts your final binary together after previous compilation.

A linker is a tool that combines compiled code from various files into a single executable or library. It merges object files, resolves references, assigns memory addresses, and links necessary libraries. This process ensures modular development, code reuse, and efficient memory management. In Rust, cargo build automates these tasks for you.

Let’s tackle this bottleneck!

In most cases you will have the option between Gold/GCC(the Gnu C compiler) and LLD

Available linkers:

  1. LLD (LLVM Linker):

    • Pros:
      • Fast: LLD is designed to be fast and efficient, making it a good choice for projects that require speed.
      • Modular: LLD is highly modular and extensible, which makes it easy to integrate with other tools and frameworks.
      • Modern: LLD is a modern linker that supports many of the features available in LLVM, such as profile-guided optimization and codegen-level tuning.
    • Cons:
      • Limited features: Compared to GNU linker (ld) and Gold, LLD has fewer features and options.
      • Less mature: LLD is a relatively new linker and may still have some bugs and missing features.
  2. GCC (GNU Compiler Collection):

    • Pros:
      • Mature: GCC has been around for a long time and has a large user base, which means it has many features and optimizations.
      • Compatible: GCC is available on many platforms and has extensive support for different architectures and operating systems.
    • Cons:
      • Slow: GCC is generally slower than LLD and Gold, especially for large projects.
      • Monolithic: GCC has a monolithic design, which can make it more difficult to integrate with other tools and frameworks.
  3. Gold:

    • Pros:
      • Fast: Gold is known for its speed and is often faster than traditional linkers like GNU linker (ld).
      • Lightweight: Gold is designed to be lightweight and scalable, making it suitable for large projects.
    • Cons:
      • Limited platform support: Gold has limited platform support compared to other linkers like LLD and GCC.
      • Less feature-rich: Gold may lack some advanced features available in other linkers.
  4. LD (GNU Linker, default):

    • Pros:
      • Mature: LD has been around for a long time and has a large user base, which means it has many features and optimizations.
      • Compatible: LD is available on many platforms and has extensive support for different architectures and operating systems.
    • Cons:
      • Slow: LD is generally slower than LLD and Gold, especially for large projects.
      • Monolithic: LD has a monolithic design, which can make it more difficult to integrate with other tools and frameworks.
  5. Mold (A Modern Linker):

    • Pros:

      • Faaast: Mold has some promosing benchmarks:
      • Modern: Made to be a faster linker than the
    • Cons:

      • Newer: has not been around for so long.
      • Less features with minimal platform support compared to lld.
      • More memory usage: Mold tries to utilize all available cores which makes it eat more ram than lld.

In general, LLD is a good stable choice for projects that require speed and efficiency, while GCC and Clang are good choices for projects that require compatibility and a wide range of features. If you’re using the default linker (ld), you may want to consider switching to LLD for a bit faster and stable longtime. If speed is your priority, we would advice you to try out Mold.

Note: As of last month(https://github.com/rust-lang/rust/pull/124129) nightly is using lld as default linker.

Links:
https://github.com/rui314/mold
https://lld.llvm.org/
https://gcc.gnu.org/
https://github.com/rust-lang/rust/issues/71515
https://clang.llvm.org/get_started.html
https://en.wikipedia.org/wiki/Clang
https://en.wikipedia.org/wiki/GNU_Compiler_Collection

How can I change my linker? #

Head over to your .cargo/config.toml file and set your linker:

LLD on Linux: #
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "linker=clang", "-C", "link-arg=-fuse-ld=lld"]
LLD MacOS: #
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/opt/llvm/bin/ld64.lld"]
LLD Windows: #
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
[target.x86_64-pc-windows-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

Mold: #

We do recommend our readers to checkout and use the mold linker, as the performance is much better:

Mold Linux: #
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]

Keep DWARF(Debugging With Attributed Record Formats) data seperate with split-debuginfo #

We can choice to not add debug data into our binary by setting the split-debuginfo flag:

rustflags = [
"-C",
"split-debuginfo=unpacked",
]

Read more about split-debuginfo:
https://doc.rust-lang.org/cargo/reference/profiles.html#split-debuginfo

Cranelift | cg_clif #

Cranelift is a code generation library used by cg_clif, a backend for the Rust compiler. It is designed to prioritize fast compilation times, making it an excellent choice for improving development speed.

  • Speed-Focused Code Generation: Cranelift generates machine code much faster than LLVM by focusing on lightweight and efficient code generation rather than heavy optimizations.
  • Quick Rebuilds: Supports incremental compilation, which allows faster rebuilds when only parts of the codebase change.

To enable the Cranelift compiler backend for your Rust project, follow these steps:

  1. Open your .cargo/config.toml file and add the following lines:
[unstable]
codegen-backend = true

[profile.dev]
codegen-backend = "cranelift"
  1. Next, open your Cargo.toml file and add the following line at the beginning of the file:
cargo-features = ["codegen-backend"]
  1. Save both files and recompile your project. Cranelift should now be used as the codegen backend for your project, potentially leading to faster compilation times and better optimization.

By adding these configurations, you enable the Cranelift compiler backend for your Rust project, allowing it to take advantage of the optimizations and speed improvements that Cranelift provides. Remember to recompile your project after making these changes to see the benefits.

Read more about Cranelift:
https://cranelift.dev/
https://github.com/rust-lang/rustc_codegen_cranelift

Slim down your codebase: #

In some cases, extra dependencies sneak into Cargo.toml. Having to compile large macro’s/chunks of code that we do not need is just slowing us down. If you are not using part of the features of a crate, disable does feature flags and only enable what your code is utilizing.

  • Use default-features = false: Set default-features = false in your Cargo.toml file to avoid accidentally enabling features you don’t need.

Find unused dependencies: #

Ferris with machete

We can easily detect unused dependencies that have snuck into our code base with cargo machete.

cargo machete --with-metadata
Analyzing dependencies of crates in this directory...
cargo-machete found the following unused dependencies in .:
threadbag -- ./Cargo.toml:
	tokio_util
	tracing
	tracing_subscriber

If you believe cargo-machete has detected an unused dependency incorrectly,
you can add the dependency to the list of dependencies to ignore in the
`[package.metadata.cargo-machete]` section of the appropriate Cargo.toml.
For example:

[package.metadata.cargo-machete]
ignored = ["prost"]

Done!

Cache your dependencies #

If you have a Continous Integration runner setup for your rust project, a great time-saver is to cache dependencies so we do not need to rebuild the entire repository every new commit.

A tool for caching Rust dependencies has been developed by Mozilla called sscache.

Add sscache to your github workflows:

      # Cache Rust toolchain and dependencies
      - name: Cache Rust toolchain and dependencies
        uses: actions/cache@v2
        with:
          path: |
            ~/.cargo/
            ./target/
          key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }}

Do you have any more tips to speed up compiling Rust? #

Send us an email at info rust dot careers