-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cranelift-codegen-meta is slowish to build #1168
Comments
I don't know much about this crate and haven't really analyzed it much before, but massive functions are known to cause perfomance issues in LLVM, mostly because we can't parallelize anything about them. Crates have been found in the past to be 90% dominated by one function, and the compile time of the crate drastically increases when the functions become smaller. If the two functions there are abnormally large the best thing to do for compile times is probably to figure out a way to shrink the functions and let LLVM take care of inlining and such as necessary. |
Those functions (and maybe also ISA-specific ones like |
Fwiw, I've looked into this a bit today. Unfortunately I misread the initial comment and thought that @glandium How did you find out this function was the biggest offender? I found about I tried to group x86 encodings by instruction category. Names and groups to be bikeshedded of course, but it seems fairly consistent. Regarding instruction definitions, things needs to be done carefully there: bindings for typevars and operands are consistently rewritten in the file (which was a shortcut taken when porting the meta code from Python). So I've tried to make it so that the split function wouldn't redefine these bindings, by reducing their lifetimes (through the use of smaller block scopes), and define typevars/operands very closely to the instruction definition. If anybody wanted to pick up this work, they should do the same to clean this technical debt. Diffing the output of the meta crate generated folder before/after the patch only showed differences of orderings in Encodings, and not a single difference in instructions. It's a bit unfortunate we can't just sort the encodings once they're defined, because they store their index into the containing vector (not sure why...). This is dumb, repetitive work, but wallclock measurements show a 3% speedup in compile time (with Rust stable), so it's definitely worth continuing. If anybody wants to take the rest of this over, please feel free to do so! |
Yeah, definitely not fun work. I merged bytecodealliance/cranelift#1322 and here are things that I think remain:
|
I was digging in to this a bit today, and to give a bit of an idea of what's going on, here's some data for this: First I executed the following to gather data: $ cargo +nightly rustc --release -p cranelift-codegen-meta -- -Z self-profile -C save-temps -Z time-passes 2>&1 | tee out.log That basically compiled this crate with a bunch of extra flags to help debugging later. Next I used the $ ../measureme/target/release/crox ./cranelift_codegen_meta-17489 --collapse-threads --minimum-duration 10 which gave this picture: From this it's clear that there's 1 CGU which is taking forever. Thread 11 performs initial optimizations and Thread 2 later picks it up for ThinLTO passes. Because it's taking so long your machine is basically sitting idle while it's optimizing that CGU except for one core, which generally isn't great. Unfortunately I don't know of a great way to go from this graph to which CGU that is. To do that I cross-referenced the graphical timing data with the output of Next I had a different tool which I wrote for something else a long time ago which yielded the number of instructions per function in this CGU:
so clearly the In any case that's at least one way to find offenders for what takes so long in a crate. Also FWIW, some reasons why
Overall it's just a really really big function that LLVM wastes tons of time trying to optimize when in fact it's probably only called once-per-program and there's really no reason to optimize it. The best way to fix it is likely to break it up into many more little functions which should be much more digestable for LLVM. |
Maybe set |
Thanks for the detailed approach @alexcrichton !
|
Removing the old style backends significantly improved compile tines for the meta crate. If I recall correctly it now only takes like 2s to compile. |
This is especially a problem when building with opt-level=2, since cargo doesn't know to apply the optimization level only to target code, while cranelift-codegen-meta is host code.
Interestingly, the build time for the cranelift-codegen-meta is dominated by LLVM (80 to 90% of the time), mostly spent between multiple LLVM module passes, LTO passes and codegen passes.
This seems to be mostly caused by the size of
shared::instructions::define
andshared::legalize::define
.Cc: @alexcrichton
The text was updated successfully, but these errors were encountered: