Live data from Hacker News

Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

feldera.com

61–70 of 89 posts

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#62

> Given that we now fully utilize 128 threads or 64 cores for pretty much the entire compile time, we can do a back of the envelope calculation for how long it should take: 25 min / 128 = 12 sec (or maybe 24 sec since hyper-threads aren't real cores). Yet it takes 170s to compile everything. Amdahl’s Law would like to have a word.

Amdahls law is about coordination costs, so either you would expect cores to be starved or lots pf extra coordination-related compute to be happening, which, i guess is not totally crazy since there are that many crates, but as a first guess OP's back of the envelope is fine

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#63
post #45

Earlier quoted context omitted.

> Now it still seems strange (as in it looks like a performance bug) that most times rust was stuck in just one threat (instead of e.g. 8). Agreed, seems like there are some rustc performance bugs at play here.

I haven't dug into the details, but it may not even be a performance bug, depending on how you define 'bug': the Rust compiler is not fully parallel itself yet. That's a bug in the sense of something that needs to be improved and fixed, but isn't one in the sense of "unexpected bad behavior".

the thing is:

codegen-units defaults to 16 in release builds, and by far the most time in the "passes" list is spend in LLVM passed (which is was codegen-units parallelizes),so most times it shouldn't be stuck with 1 high load core (even if it's not 16 all the time).

so it looks a lot like something is prevented the intended codegen parallelization of the crate

Through it indeed might not have been a bug, e.g. before the change in generation to split it across crates source code might have been in a way where it can't split the crate into multiple units. Or maybe something made rust believe splitting it is a bad idea, e.g. related to memory usage or similar.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#64

Earlier quoted context omitted.

I haven't dug into the details, but it may not even be a performance bug, depending on how you define 'bug': the Rust compiler is not fully parallel itself yet. That's a bug in the sense of something that needs to be improved and fixed, but isn't one in the sense of "unexpected bad behavior".

the thing is: codegen-units defaults to 16 in release builds, and by far the most time in the "passes" list is spend in LLVM passed (which is was codegen-units parallelizes),so most times it shouldn't be stuck with 1 high load core (even if it's not 16 all the time). so it looks a lot like something is prevented the intended codegen parallelization of the crate Through it indeed might not have been a bug, e.g. before…

Ah yeah, that does sound like a bug to me; it’s the earlier stages that I’m thinking of that aren’t parallel yet.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#65
post #29

Earlier quoted context omitted.

> Also note that D, a language as complex as C++ While D is complex, it's a different beast than Rust. I suspect think various checks, from lifetime to trait resolution, might make it more complex to parallelize than C++.

Rust is already able to do more fine-grained parallel compilation than C or C++, at least in the codegen step. The "codegen units" concept doesn't work for those languages.

> The "codegen units" concept doesn't work for those languages.

Sure it does. Or at least could, depending on what you mean by that term. See, e.g., GCC's -flto-partition option, which supports various partitioning strategies for parallel LTO codegen.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#66
post #18

Earlier quoted context omitted.

I have been using make's -j flag to compile C++ projects with great success. The main point being, Rust focuses on the wrong type of optimizations.

codegen units is quite the same as the `-j` flag cargo has the `-j` flag and defaults it to #cpus (logical cpus), so it's by default using the most times most optimal choice there And this will parallelize the compilation of all "jobs", roughly like with make. Where a job is normally (oversimplified) compiling one code unit into one object file (.o). And cargo does that too. The problem is that where rust and C/C++ (…

> Hence why rust internally split one "semantic code unit" into multiple internal code units passed to LLM.

And the same has happened in C and C++ land, albeit in the opposite direction, where multiple compilation units can be optimized together, i.e. LTO. See, e.g., GCC's -flto-partition option for selecting strategies for partitioning symbols for LTO.

Also note that you can manually partition LTO in your Makefile by grouping compilation units into object files to be individually LTO'd.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#67
post #18

Earlier quoted context omitted.

I have been using make's -j flag to compile C++ projects with great success. The main point being, Rust focuses on the wrong type of optimizations.

Which is exactly what this project is now able to do. Your parallel make jobs don't help if you have one gigantic compilation unit, as they originally did.

LTO can be parallelized, both implicitly from the Makefile, and also within the compiler. GCC's -flto itself takes an optional argument to control the number of parallel threads/jobs. See also the -flto-partition option for selecting symbol partitioning strategies.

Rust/Cargo does this automagically, except the only control you have are the crate and module boundaries. The analogous approach for C is to (optionally) manually group compilation units into a smaller set of object files in your Makefile, LTO'ing each object file in parallel (make -j), and then (optionally) telling the compiler to partition and parallelize a second time on the backend. Which is what Rust does, basically, IIUC--a crate is nominally the LTO codegen unit, except to speed up compilation Rust has heuristics for partitioning crates internally for parallel LTO.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#68

> back of the envelope calculation for how long it should take: 25 min / 128 = 12 sec (or maybe 24 sec since hyper-threads aren't real cores). Yet it takes 170s to compile everything. I'd aim for this linear speedup for compiling (sans overhead to compile a small crate), but the linking part won't be faster, maybe even slower. Maybe a slightly bigger envelope can tell you how much performance is there to extract and…

Yeah, I was working on this project to generate a python C-API module from the SVG schema and found when I generated one huge file (as opposed to one file per class) the compilation times were significantly faster. Or maybe it was generating the C++ SVG library, don't quite remember which one was super slow as I did both at around the same time since the code changes between the two were minimal.

Looks like I settled on the slower 'one class per file' compilation method for whatever reason, probably because generating a 200k+ file didn't seem like such a good idea.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#69
The dependency costs seem to be self-evident but perhaps it would be enlightening to see a comparison of the energy costs, as well as the time costs and storage costs, of compiling Rust programs versus their C equivalents. For example, comparison might reveal that there is no difference and no trade-off or that any differences and trade-offs are small enough to be worth making in the interest of some higher purpose.

Re: Cutting down Rust compile times from 30 to 2 minutes with one thousand crates

#70
post #65

Earlier quoted context omitted.

Rust is already able to do more fine-grained parallel compilation than C or C++, at least in the codegen step. The "codegen units" concept doesn't work for those languages.

> The "codegen units" concept doesn't work for those languages. Sure it does. Or at least could, depending on what you mean by that term. See, e.g., GCC's -flto-partition option, which supports various partitioning strategies for parallel LTO codegen.

LTO is distinct, and happens after what codegen units does.

That said, it is a more fine grained parallelism, for sure. Rust does LTO as well as codegen-units.

Really, as you gesture towards, on some level, this is all semantics: our linkers are also basically compilers too, at this point.

Post reply on HN