← Back to context

Comment by rtpg

17 hours ago

> Rust has made several language design decisions that make compiler performance difficult

Do you have a list off the top of your head/do you know of a decent list? I've now read many "compiler slow" thoughtpieces by many people and I have yet to see someone point at a specific feature and say "this is just intrinsically harder".

I believe that it likely exists, but would be good to know what feature to get mad at! Half joking of course

You can have your house built fast, cheap, or well. Pick two; or a bit of all three that adds up to the same effort required. You can't have all three.

You can't have a language with 100% of the possible runtime perf, 100% of the possible compile speed and 100% of the possible programmer ease-of-use.

At best you can abuse the law of diminishing returns aka the 80-20 rule, but that's not easy to balance and you run the risk of creating a language that's okay at everything, but without any strong selling points, like the stellar runtime performance Rust is known for.

So a better way to think about it is: Given Rust's numerous benefits, is having subpar compilation time really that big of a deal?

  • > Given Rust's numerous benefits, is having subpar compilation time really that big of a deal?

    As someone who uses Rust as a daily driver at work at zed.dev (about 600K LoC of Rust), and Zig outside of work on roc-lang.org (which was about 300K LoC of Rust before we decided to rewrite it in Zig, in significant part because of Rust's compilation speed), yes - it is an absolutely huge deal.

    I like a lot of things about Rust, but its build times are my biggest pain point.

Rust heavily uses value types with specialized generics, which explodes the work needed by the compiler. It can - sometimes - improve performance. But it always slows down compilation.

Brian Anderson wrote up his thoughts here, and it's a good intro to the topic: https://www.pingcap.com/blog/rust-compilation-model-calamity...

Let's dig into this bit of that, to give you some more color:

> 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.

Rust decided to go with the classic separate compilation model that languages like C use. Let's talk about that compared to Zig, since it was already brought up in this thread.

So imagine we have a project, A, and it depends on B. B is a huge library, 200,000 lines of code, but we only use one function from it in A, and that function is ten lines. Yes, this is probably a bad project management decision, but we're using extremes here to make a point.

Cargo will compile B first, and then A, and then link things together. That's the classic model. And it works. But it's slow: rust had to compile all 200,000 lines of code in B, even though we only are gonna need ten lines. We do all of this work, and then we throw it away at the end. A ton of wasted time and effort. This is often mitigated by the fact that you compile B once, and then compile A a lot, but this still puts a lot of pressure on the linker, and generics also makes this more complex, but I'm getting a bit astray of the main point here, so I'll leave that alone for now.

Zig, on the other hand, does not do this. It requires that you compile your whole program all at once. This means that they can drive the compilation process beginning from main, in other words, only compile the code that's actually reachable in your program. This means that in the equivalent situation, Zig only compiles those ten lines from B, and never bothers with the rest. That's just always going to be faster.

Of course, there are pros and cons to both of these decisions, Rust made the choice it did here for good reasons. But it does mean it's just going to be slower.