Settings

Theme

How to speed up the Rust compiler in 2022

nnethercote.github.io

174 points by nnethercote 4 years ago · 22 comments

Reader

nicoburns 4 years ago

I would like to add for anyone who doesn't use Rust, that these performance improvements in the Rust compiler are not just well written up, but are very meaningful in the real world. Subjectively (I've not measured scientifically), I'd say Rust compile times are ~1/2 what they were a few years ago. With that, incremental compilation, LLD, and a new machine my experience of Rust compile times has been completely revolutionised. There's still more work to be done, but a lot has been achieved here.

  • pjmlp 4 years ago

    Indeed, it has become much more usable on my travel netbook (Asus 1215 B).

    Nowadays it is really tolerable.

brutal_chaos_ 4 years ago

I tried....and it didn't work.

Thank you. I appreciate the description of what was tried and didn't work or didn't work as well as hoped. We need more of this so I don't waste time (or you don't too).

twic 4 years ago

> #93066: The Decoder trait used for metadata decoding was fallible, using Result throughout. But decoding failures should only happen if something highly unexpected happens (e.g. metadata is corrupted) and on failure the calling code would just abort. This PR changed Decoder to be infallible throughout—panicking immediately instead of panicking slightly later—thus avoiding lots of pointless Result propagation, for wins across many benchmarks of up to 2%.

Interesting insight into the cost of Result-based error handling.

  • jcelerier 4 years ago

    not surprising though, this is the exact same scenario than with C++ exceptions. if your code is on the happy path, they are consistently faster than error codes

    • dthul 4 years ago

      I was wondering whether the Rust compiler could treat Result enums specially and implement them as panics under the hood. But I guess such optimizations are not really feasible because they might change behavior when unwinding through FFI for example. Not even speaking of the possibility that each stack frame is able to convert the error type, unlike exceptions.

      • Inityx 4 years ago

        It would also change the behavior of panicking destructors on the unwind path, since a second panic during an unwind causes an abort instead of another unwind

tentacleuno 4 years ago

I really wonder how much these changes, especially seeing the numbers (4%, 5%), add up at scale. For example, from reading a comment on Hacker News[0], I'm told that small changes can save companies millions and millions on servers. It saves power, too.

[0]: https://news.ycombinator.com/item?id=30461201

  • gameswithgo 4 years ago

    Most of these changes speed up the rust compiler rather than how fast the compiled code runs, but in some cases the changes do both. In general, often the total improvement is less than the sum of it's parts, because of diminishing returns. Sometimes though the total savings is MORE than the sum of it's parts. An interesting example I recall from C# work is when one develop made an improvement to the JIT which reduced the number of instructions in List.Add, this made an insignificant improvement in runtime. Another develop also tweaed the List.Add function directly, reducing the number of instructions as well, and again another insignificant improvement in run time. When put together though, the function became small enough to get inlined, which lead to a big improvement in runtime. As well as avoiding the memory hop, sometimes inlining lets the compiler find other optimizations.

  • the8472 4 years ago

    Those are mostly compiler speedups, fewer compiled-code speedups. Since most code is executed more often than it is compiled the fleet-level savings will be much smaller. It helps with CI jobs and developer-cycles though.

ncmncm 4 years ago

Anybody interested in speeding up the Rust compiler should be looking into generating a new JITted parser after each macro definition, and jumping into it to parse the remaining (and any other affected) code.

The time to compile a new parser ought to be much less than feeding literally all tokens into a runtime macro-definition interpreter.

Similar infrastructure might help with generics type calculus.

  • fanf2 4 years ago

    Rust macros only require special parsing for macro arguments, so most tokens don’t need to touch the macro engine.

tiddles 4 years ago

These posts are always a pleasure to read. it's great to see continuous work on improving compiler performance, and especially with good results too.

mrich 4 years ago

Recently I integrated the mold linker into a Rust project and linking of the main executable saw a speedup of 3.35x. This is pretty helpful for incremental builds where you typically edit one file and then build, so you have one compile and then the link. There is no build-job parallelism at that point so every second counts :)

mamcx 4 years ago

One thing that I wish were, at least, explored is how make Rust simpler.

I bet Rust syntax and some complex and divergents ways of typing are major implications on how Rust compile (you can see why, when comparing to Pascal). Also, modules, macros must be, IMHO, major faults here.

The other things: San, Quote, Serde? That stuff must be bring home. At minimum, the bare traits and some basic machinery.

Other thing: The orphan rule means exist a lot of places with redundant code to tie types that cause extra compilation efforts (because n-crates must pull n-crates to impl traits!).

  • leshow 4 years ago

    Simpler in what way? There are efforts to make writing Rust simpler, to make lifetime elision better or have the borrowck accept more valid programs.

    I don't think it's reasonable to expect Rust to explore removing language features though.

    • mamcx 4 years ago

      Example:

      - Exist 2 ways to do macros.

      - Exist different ways to annotate traits (impl and the long way)

      - Alias work, but not everywhere

      - The module system is too complicated

      - You have pub and pub(crate). The second must have been the default for types on a crate because the way how things are because modules

      - The trait system is full of "but" that make it not as simple to use in practice (ej: Is too common traits require extra work because this or that rule in THIS case must be appeased, and also, is where is more obvious things are in disconnect with the rest of the lang)

      This as the most obvious.

      • saghm 4 years ago

        > - The trait system is full of "but" that make it not as simple to use in practice (ej: Is too common traits require extra work because this or that rule in THIS case must be appeased, and also, is where is more obvious things are in disconnect with the rest of the lang)

        Can you give a concrete example of what you mean here? I don't doubt that you have a legitimate criticism, but it's phrased in a way that I genuinely am not able to understand

        • estebank 4 years ago

          This is a great example of where the way people use the word "simple" comes along with lots of caveats and mean different things to each one of us. Increasing simplicity in the user experience (by removing seemingly arbitrary restrictions to the type system) mean increasing the complexity of the implementation. We've already seen that with match ergonomics, the borrow checker and async: "adding complexity" results in "a simpler to use" language.

      • leshow 4 years ago

        You have just listed a bunch of things as if they are evidence of something, but they are not.

ZeroGravitas 4 years ago

Does anyone do this kind of analysis but across well used libraries for a language?

Similar to how in this article they compiled a whole bunch of rust projects and flagged up the areas that were slow to compile for specific crates, does anyone do analysis that says this dependency shows up in a lot of hot paths when running benchmarks or test code and then get someone to apply this kind of systems level thinking to it?

It seems at least one fix was for a hashing library dependency and may have wider benefits, but surely that applies to more things too?

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection