Comment by taylorallred
1 day ago
@AndyKelley I'm super curious what you think the main factors are that make languages like Zig super fast at compiling where languages like Rust and Swift are quite slow. What's the key difference?
1 day ago
@AndyKelley I'm super curious what you think the main factors are that make languages like Zig super fast at compiling where languages like Rust and Swift are quite slow. What's the key difference?
I'm not Andrew, but Rust has made several language design decisions that make compiler performance difficult. Some aspects of compiler speed come down to that.
One major difference is the way each project considers compiler performance:
The Rust team has always cared to some degree about this. But, from my recollection of many RFCs, "how does this impact compiler performance" wasn't a first-class concern. And that also doesn't really speak to a lot of the features that were basically implemented before the RFC system existed. So while it's important, it's secondary to other things. And so while a bunch of hard-working people have put in a ton of work to improve performance, they also run up against these more fundamental limitations at the limit.
Andrew has pretty clearly made compiler performance a first-class concern, and that's affected language design decisions. Naturally this leads to a very performant compiler.
> 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?
1 reply →
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.
Basically, not depending on LLVM or LLD. The above is only possible because we invested years into making our own x86_64 backend and our own linker. You can see all the people ridiculing this decision 2 years ago https://news.ycombinator.com/item?id=36529456
LLVM isnt a good scapegoat. A C application equivalent in size to a rust or c++ application will compile an order of magnitude quicker and they all use LLVM. I'm not a compiler expert, but it doesn't seem right to me that the only possible path to quick compilation for Zig was a custom backend.
Be that as it may, many C compilers are still an order of magnitude faster than LLVM. Probably the best example is tcc, although it is not the only one. C is a much simpler language than rust, so it is expected that compilation should take less time for C. That doesn’t mean llvm isn’t a significant contributor to compilation speed. I believe cranelift compilation of rust is also much faster than the llvm path
1 reply →
It will compile an order of magnitude quicker because it often doesn't do the same thing - e.g. functions that are aggressively inlined in C++ or Rust or Zig would be compiled separately and linked normally, and generally there's less equivalent of compile-time generics in C code (because you have to either spell out all the instantiations by hand or use preprocessor or a code generator to do something that is two lines of code in C++).
The Rust folks have cranelift and wild BTW. There are alternatives to LLVM and LLD, even though they might not be as obvious to most users.
what is even the point of quoting reactions from two years ago?
this is a terrible look for your whole community
Honestly I think it's good to highlight it. As a industry we're too hampered by "Don't even try that, use the existing thing" and it's causing these end results.
I'm also curious because I've (recently) compiled more or less identical programs in Zig and Rust and they took the same amount of time to compile. I'm guessing people are just making Zig programs with less code and fewer dependencies and not really comparing apples to apples.
Zig is starting to migrate to custom backends for debug builds (instead of using LLVM) plus incremental compilation.
All Zig code is built in a single compilation unit and everything is compiled from scratch every time you change something, including all dependencies and all the parts of the stdlib that you use in your project.
So you've been comparing Zig rebuilds that do all the work every time with Rust rebuilds that cache all dependencies.
Once incremental is fully released you will see instant rebuilds.
When does this land in Zig? Will aarch64 be supported?
3 replies →
One difference that Zig has is that it doesn't have multiline comments or multiline strings, meaning that the parser can parse any line correctly without context. I assume this makes parallelization trivial.
There is ino operator overloading like C, so A + B can only mean one thing.
You can't redeclare a variable, so foo can only map to one thing.
The list goes on.
Basically it was designed to compile faster, and that means many issues on Github have been getting rejected in order to keep it that way. It's full of compromises.