Top
Best
New

Posted by Bogdanp 6/26/2025

Why is the Rust compiler so slow?(sharnoff.io)
303 points | 428 commentspage 4
charcircuit 6/26/2025|
Why doesn't the Rust ecosystem optimize around compile time? It seems a lot of these frameworks and libraries encourage doing things which are slow to compile.
int_19h 6/26/2025||
It would be more accurate to say that idiomatic Rust encourages doing things which are slow to compile: lots of small generic functions everywhere. And the most effective way to speed this up is to avoid monomorphization by using RTTI to provide a single generic compiled implementation that can be reused for different types, like what Swift does when generics across the module boundary. But this is less efficient at runtime because of all the runtime checks and computations that now need to be done to deal with objects of different sizes etc, many direct or even inlined calls now become virtual etc.

Here's a somewhat dated but still good overview of various approaches to generics in different languages including C++, Rust, Swift, and Zig and their tradeoffs: https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models...

nicoburns 6/26/2025|||
It's starting to, but a lot of people are using Rust because they need (or want) the best possible runtime performance, so that tends to be prioritised a lot of the time.
steveklabnik 6/26/2025|||
The ecosystem is vast, and different people have different priorities. Simple as that.
kzrdude 6/27/2025||
Lots of developers in the ecosystem avoid proc macros for example. But going as far as avoiding monomorphisation and generics is not that widespread
hdjdbdirbrbtv 7/1/2025||
As a professional rust dev, I will say this, I don't notice. Generally because I am doing partial builds mostly. And when I am not the m1 max I have is fast enough to compile the project.

Really there are much larger issues that I have to deal with that build time does not affect.

WalterBright 6/27/2025||
I suspect that it is because the borrow checker needs to do data flow analysis. DFA is normally done in the optimizer, and we all know that optimized builds are quite a bit slower, and that is due to DFA.

DFA in the front end means slow.

steveklabnik 6/27/2025|
As said many times in this thread already, the borrow checker is virtually never a significant amount of time spent while compiling.

Also, the article goes into great depth as to what is happening there, and the borrow checker never comes up.

WalterBright 6/27/2025||
That makes me curious as to how Rust does the DFA. What I do is construct the data flow equations as bit vectors, which are then solved iteratively until a solution is converged on.

Doing this on the whole program eats a lot of memory.

steveklabnik 6/27/2025||
I'm not an expert on this part of the compiler, but what I can tell you is that Rust uses multiple forms of IR, and the IR that the borrow checker operates on (https://rustc-dev-guide.rust-lang.org/mir/index.html) already encodes control flow. So doing that construction, at least, is just a normal part of the compiler, and isn't part of the borrow checker itself.

However, in my understanding, it takes that IR, does build a borrow graph, computes liveliness, and then does inference. There's been a number of details in how this has changed over the years, but it's vaguely datalog shaped.

WalterBright 6/28/2025||
Because there are cycles in the flow graph, I do not see how liveness can be computed in general without iteration and convergence on a solution.
senderista 6/26/2025||
WRT compilation efficiency, the C/C++ model of compiling separate translation units in parallel seems like an advance over the Rust model (but obviously forecloses opportunities for whole-program optimization).
woodruffw 6/26/2025||
Rust can and does compile separate translation units in parallel; it's just that the translation unit is (roughly) a crate instead of a single C or C++ source file.
EnPissant 6/26/2025||
And even for crates, Rust has incremental compilation.
Devasta 6/26/2025||
Slow compile times are a feature, get to make a cuppa.
zozbot234 6/26/2025|
> Slow compile times are a feature

xkcd is always relevant: https://xkcd.com/303/

randomNumber7 6/26/2025||
On the other hand you get mentally insane if you try to work in a way that you do s.th. usefull during the 5-10 min compile times you often have with C++ projects.

When I had to deal with this I would just open the newspaper and read an article in front of my boss.

PhilipRoman 6/27/2025||
Slow compile times really mess with your brain. When I wanted to test two different solutions, I would keep multiple separate clones (each one takes about 80GB, mind you) and do the manual equivalent of branch prediction by compiling both, just in case I needed the other one as well.
jeden 6/27/2025||
why rust compiler create so BIG executable!
RS-232 6/26/2025||
Is there an equivalent of ninja for rust yet?
steveklabnik 6/26/2025|
It depends on what you mean by 'equivalent of ninja.'

Cargo is the standard build system for Rust projects, though some users use other ones. (And some build those on top of Cargo too.)

fschuett 6/27/2025||
For deploying Rust servers, I use Spin WASM functions[1], so no Docker / Kubernetes is necessary. Not affiliated with them, just saying. I just build the final WASM binary and then the rest is managed by the runtime.

Sadly, the compile time is just as bad, but I think in this case the allocator is the biggest culprit, since disabling optimization will degrade run-time performance. The Rust team should maybe look into shipping their own bundled allocator, "native" allocators are highly unpredictable.

[^1]: https://www.fermyon.com

jmyeet 6/26/2025||
Early design decisions favored run-time over compile-time [1]:

> * Borrowing — Rust’s defining feature. Its sophisticated pointer analysis spends compile-time to make run-time safe.

> * Monomorphization — Rust translates each generic instantiation into its own machine code, creating code bloat and increasing compile time.

> * Stack unwinding — stack unwinding after unrecoverable exceptions traverses the callstack backwards and runs cleanup code. It requires lots of compile-time book-keeping and code generation.

> * Build scripts — build scripts allow arbitrary code to be run at compile-time, and pull in their own dependencies that need to be compiled. Their unknown side-effects and unknown inputs and outputs limit assumptions tools can make about them, which e.g. limits caching opportunities.

> * Macros — macros require multiple passes to expand, expand to often surprising amounts of hidden code, and impose limitations on partial parsing. Procedural macros have negative impacts similar to build scripts.

> * LLVM backend — LLVM produces good machine code, but runs relatively slowly. Relying too much on the LLVM optimizer — Rust is well-known for generating a large quantity of LLVM IR and letting LLVM optimize it away. This is exacerbated by duplication from monomorphization.

> * Split compiler/package manager — although it is normal for languages to have a package manager separate from the compiler, in Rust at least this results in both cargo and rustc having imperfect and redundant information about the overall compilation pipeline. As more parts of the pipeline are short-circuited for efficiency, more metadata needs to be transferred between instances of the compiler, mostly through the filesystem, which has overhead.

> * Per-compilation-unit code-generation — rustc generates machine code each time it compiles a crate, but it doesn’t need to — with most Rust projects being statically linked, the machine code isn’t needed until the final link step. There may be efficiencies to be achieved by completely separating analysis and code generation.

> * Single-threaded compiler — ideally, all CPUs are occupied for the entire compilation. This is not close to true with Rust today. And with the original compiler being single-threaded, the language is not as friendly to parallel compilation as it might be. There are efforts going into parallelizing the compiler, but it may never use all your cores.

> * Trait coherence — Rust’s traits have a property called “coherence”, which makes it impossible to define implementations that conflict with each other. Trait coherence imposes restrictions on where code is allowed to live. As such, it is difficult to decompose Rust abstractions into, small, easily-parallelizable compilation units.

> * Tests next to code — Rust encourages tests to reside in the same codebase as the code they are testing. With Rust’s compilation model, this requires compiling and linking that code twice, which is expensive, particularly for large crates.

[1]: https://www.pingcap.com/blog/rust-compilation-model-calamity...

b0a04gl 6/26/2025|
[dead]
More comments...