Live data from Hacker News

Rust compiler performance

kobzol.github.io

211–220 of 264 posts

Re: Rust compiler performance

#211
post #40

Not related to the article, but after years of using Rust, it still is a pain in the ass. While it may be a good choice for OS development, high frequency trading, medical devices, vehicle firmware, finance software, or working on device drivers, it feels way overkill for most other general domains. On the other hand, I learned Zig and Go both over a weekend and find they run almost as fast and don't suffer from memo…

This comment would have been more useful with some qualification of why that’s the case. The language, tooling, library ecosystem? Something else?

For Go, it's a design decision. From the start, they strived to make compilation as fast as possible.

https://en.wikipedia.org/wiki/Go_(programming_language)#Desi...

Re: Rust compiler performance

#212
post #65
post #53

Earlier quoted context omitted.

If only these were common problems that were difficult to otherwise avoid.

Rust feels like wearing a giant bubble just to go outside safely. C++ feels like driving a car. Dangerous but doable and often necessary and usually safe. (Forth feels like being drunk?)

Any non-trivial C++ program includes unchecked int casts

Re: Rust compiler performance

#213
post #147

Earlier quoted context omitted.

> Why not both? For better and worse, the Rust project is a "show-up-ocracy": the work that gets done is the one that volunteers (or paid employees from a company donating their time to the project) spend time doing. It's hard in open source projects to tell people "this is important and you must work on it", but rather you have to convince people that it is important and have to hope they will have the time, inclina…

> For better and worse, the Rust project is a "show-up-ocracy" So it's an OSS project?

Linux and GCC are OSS projects but CPU companies pour work into their development because it's a strict requirement of being able to sell their products.

Re: Rust compiler performance

#214

Earlier quoted context omitted.

I recently tried using cranelift on a monorepo with a bunch of crates, and it is nothing short of amazing. Nothing broke and workspace build time went from a minute and a half to a half of a second!

Was this for a release build or a debug build?

Cranelift is only intended for debug builds, there is nothing stopping you from using it for release builds but — to the best of my knowledge — you get noticeably degraded runtime performance if you go that way.

Re: Rust compiler performance

#215
post #140

Earlier quoted context omitted.

No, language design decisions absolutely have a massive impact the performance envelope of compilers. Think about things like tokenization rules (Zig is designed such that every line can be tokenized independently, for example), ambiguous grammars (most vexing parse, lexer hack etc.), symbol resolution (e.g. explicit imports as in Python, Java or Rust versus "just dump eeet" imports as in C#, and also things whether…

The lexer hack is a C thing, and Ive rarely heard anyone complain about C compiler performance. That seems more like an argument that the grammar doesn't have that much of an impact on compiler performance as other things.

Yeah. It's exactly backwards, because good language design doesn't make anything except parsing faster. The problem is that some languages have hideously awful grammars that make things slower than they ought to be.

The preprocessor approach also generates a lot of source code that then needs to be parsed over and over again. The solution to that isn't language redesign, it's to stop using preprocessors.

Re: Rust compiler performance

#216
The biggest thing that's happened in recent time to improve Rust compiler performance was the introduction the Apple M-series chips. On my x86 machine, it'll take maybe 10 minutes for a fresh build of my project, but on my Apple machine that's down to less than a minute, even on the lower end Mac Mini. For incremental builds it only takes a few seconds. I'm fine with this amount of compilation time for what it buys me, and I don't feel it slows me down any because (and I know this sounds like cope) it gives me a minute to breathe and collect my thoughts. Sometimes I find that debugging a problem while actively coding in an interactive REPL is different from debugging offline.

I'm not sure why but the way I would explain it is when you're debugging in an interactive REPL you're always get fast incremental result, but you may be going down an unproductive rabbit hole and spinning your tires. When I hit that compile button, I'm able to take a step back and maybe see the problem from another angle. Still, I prefer a short development loop, but I do think you lose something from it.

Re: Rust compiler performance

#217
post #146

Earlier quoted context omitted.

OK, parser then: https://github.com/rust-lang/rust/tree/1.87.0/compiler/rustc... (main logic seems to be in expr.rs) vs https://github.com/ziglang/zig/blob/0.14.1/lib/std/zig/Parse... Again, for those who wish to form their own opinions.

I think a reasonable comparison would have to be DoD Rust parser vs current Rust parser. Comparing across languages isn't very useful, because Zig has very different syntax rules, and doesn't provide diagnostics near the same level as Rust does. The Rust compiler (and also its parser) spends an incredible amount of effort on diagnostics, to the point of actually trying to parse syntax from other languages (e.g. Pytho…

[edited to correct formatting]

Your points here don't really make sense. There are many ways you can apply DoD to a codebase, but by far the main one (both easiest and most important) is to optimize the in-memory layout of long-lived objects. I won't claim to be familiar with the Rust compiler pipeline, but for most compilers, that means you'd have a nice compact representation for a `Token` and `AstNode` (or whatever you call those concepts), but the code between them -- i.e. the parser -- isn't really affected. In other words, all the fancy features you describe -- macros intertwined with name resolution, parsing syntax from other languages, high-quality diagnostics -- don't care about DoD! Our approach in the Zig compiler has evolved over time, but we're slowly converging towards a style where all of the access to the memory-efficient dense representation is abstracted behind functions. So, you write your actual processing (e.g. your parser with all the features you mention) just the same; the only real difference is that when your parser wants to, for instance, get a token (as input) or emit an AST node (as output), it calls functions to do that, and those functions pull out the bytes you need into a lovely `struct` or (in Rust terms) `enum` or whatever the case may be.

Our typical style in Zig, or at least what we tend to do when writing DoD structures nowadays, is to have the function[s] for "reading" that long-lived data (e.g. getting a single token out from a memory-efficient packed representation of "all the tokens") in the implementation of the DoD type, and the functions for "writing" it in the one place that generates that thing. For instance, the parser has functions to deal with writing a "completed" AST node to the efficient representation it's building, and the AST type itself has functions (used by the next phase of the compiler pipeline, in our case a phase called AstGen) to extract data about a single AST node from that efficient representation. That way, barely any code has to actually be aware of the optimized representation being used behind the scenes. As mentioned above, what you end up with is that the actual processing phases look more-or-less identical to how they would without DoD.

FWIW, I don't think the parser is our best code here: it's one of the oldest "DoD-ified" things in the Zig codebase so has some outdated patterns and questionable naming. Personally, I'm partial to `ZonGen`[0] as a fairly good example of a "processing" phase (although I'm admittedly biased!). It inputs an AST and outputs a simple tree IR for a subset of Zig which is analagous to JSON. Then, for an example of code consuming that generated IR, take a look at `print_zoir`[1], which just dumps the tree to stdout (or whatever) for debugging purposes. The interesting logic is in `PrintZon.renderNode` in that file: note how it calls `node.get`, and then just has a nice convenient tagged union (`enum` in Rust terms) value to work with.

[0]: https://github.com/ziglang/zig/blob/dd75e7bcb1fe142f4d60dc2d...

[1]: https://github.com/ziglang/zig/blob/dd75e7bcb1fe142f4d60dc2d...

Re: Rust compiler performance

#218
post #217
post #146

Earlier quoted context omitted.

I think a reasonable comparison would have to be DoD Rust parser vs current Rust parser. Comparing across languages isn't very useful, because Zig has very different syntax rules, and doesn't provide diagnostics near the same level as Rust does. The Rust compiler (and also its parser) spends an incredible amount of effort on diagnostics, to the point of actually trying to parse syntax from other languages (e.g. Pytho…

[edited to correct formatting] Your points here don't really make sense. There are many ways you can apply DoD to a codebase, but by far the main one (both easiest and most important) is to optimize the in-memory layout of long-lived objects. I won't claim to be familiar with the Rust compiler pipeline, but for most compilers, that means you'd have a nice compact representation for a `Token` and `AstNode` (or whateve…

All great points. In case it's relevant, I will also note that the rust-analyzer parser is considerably closer to DoD than the rustc parser is: https://github.com/rust-lang/rust-analyzer/tree/a642aa8023be...

Re: Rust compiler performance

#219
post #27

Compiler performance must be considered up front in language design. It is nearly impossible to fix once the language reaches a certain size without it being a priority. I recently saw here the observation that one can often get a 2x performance improvement through optimization, but 10x requires redesigning the architecture. Rust can likely never be rearchitected without causing a disastrous schism in the community,…

One of the issue why compile times are so awful is that all dependencies must be compiled for each project. 20 different projects use the same dependency? They each need to recompile it. This is an effect of the language not having a proper ABI for compiling libraries as dynamically loadable modules, which in itself presents many other issues, including making distribution of software a complete nightmare.

If you use bazel to compile rust, it doesn't suffer from this problem. In fact you can get distributed caching as well.

Re: Rust compiler performance

#220
post #97

Earlier quoted context omitted.

> This is an effect of the language not having a proper ABI for compiling libraries as dynamically loadable modules No, this is a design decision of Cargo to default to using project-local cached artifacts rather than caching them at the user or system level. You can configure Cargo to do so if you'd like. The reason it doesn't do this by default is because Cargo gives crates great latitude to configure themselves vi…

Why can't Cargo have a system like PyPI where library author uploads compiled binary (even with their specific flags) for each rust version/platform combination, and if said binary is missing for certain combination, fallback to local compile? Imagine `cargo publish` handle the compile+upload task, and crates.io be changed to also host binaries.

A trustworthy distributed cache would also work very well for this in practice. Cargo works with sccache. Using bazel + rbe can work even better.
Post reply on HN