Skip to content
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

Support for compilers prior to Rust 1.55 causing high compile times #161

Open
nnethercote opened this issue Jun 8, 2022 · 4 comments
Open

Comments

@nnethercote
Copy link

nnethercote commented Jun 8, 2022

I have been working on Rust compiler performance, guided by this analysis document that looks at compilation of 800 popular crates. It identifies tinyvec as an interesting case, being the only crate out of those 800 to cause the diverge_cleanup function within the compiler to be very hot.

I just looked into this, and it's caused by the very large array literals in the various default methods within generated_impl.rs. I don't see an easy way to speed up the compiler's handling of these literals, and the rarity of this case suggests it's not worth much effort to improve.

Fortunately, there's an alternative solution already present in tinyvec: the rustc_1_55 feature uses const generics to avoid the need for generated_impl.rs. I just did some simple benchmarking, and tinyvec-1.6.0 compiles about twice as fast when rustc_1_55 is enabled. Here are numbers for three kinds of non-incremental builds.

build kind default rustc_1_55
check 0.633s 0.326s
debug 0.681s 0.320s
release 0.703s 0.385s

There are several possible ways to make the faster compile times more widely available.

  • Increase the minimum supported Rust version to 1.55.
  • Failing that, change the structure of the features so that the default feature set uses const generics and users with pre-1.55 versions can opt into the generated code. This would presumably require at least a minor version change.
  • Failing that, add documentation about the rustc_1_55, which currently isn't mentioned in the README or the rustdocs.

Defaults matter, so the first or second option would be preferable.

It's also possible to use a build.rs to auto-detect the Rust version being used, but compiling and running the build script takes a non-trivial amount of time and would eat into the savings, so I don't recommend doing that.

Anyway, I hope this is useful information! Fast compile times are good for everybody :) I'm happy to answer any questions you might have.

@Lokathor
Copy link
Owner

Lokathor commented Jun 8, 2022

hmmmmmmmmm

how much of this impacts rebuild times when the crate is used as a dep? I do want fast builds, but I think that if only the initial build is longer that's probably acceptable. We're only talking a difference of 0.3 seconds here after all.

Either way I'm happy to at least have the docs.

I'm much less enthused by the idea of a minimum Rust bump in a minor version but if it's just a default feature change and a person can just fiddle with their deps entry to compensate that's probably okay i guess.

@nnethercote
Copy link
Author

The saving will of course only occur when the crate is compiled/recompiled (which includes the initial build, and the initial cargo test builds, and again after any cargo clean and again after each Rust update). And yes it is a fairly small crate. On the other hand, it's rare to see 50% wins that are this easy :)

@Lokathor
Copy link
Owner

Lokathor commented Jun 8, 2022

Ah, so it's not quite like normal generics where the monomorph cost of the code is paid by the downstream crate each time it rebuilds?

@nnethercote
Copy link
Author

Oh, good point, I hadn't considered that. So it's possible the benefits are larger than I said.

gnomesysadmins pushed a commit to GNOME/librsvg that referenced this issue Apr 20, 2023
While casually reading
https://hackmd.io/mxdn4U58Su-UQXwzOHpHag (Analysis of
rustc-benchmarking-data), I noticed that it mentions
Lokathor/tinyvec#161.

The tinyvec crate, with the default set of features, can compile on
rustc < 1.55 by using large implementations of Default for [T; N].  On
Rustc >= 1.55, it can elide this code and just use const generics.
Apparently this reduces compilation time for tinyvec by about half -
which is not a lot in the context of librsvg, but let's make use of
that.

Librsvg currently requires Rust >= 1.64.0 anyway, so we can make use
of tinyvec's rustc_1_55 feature.
gnomesysadmins pushed a commit to GNOME/librsvg that referenced this issue Apr 25, 2023
While casually reading
https://hackmd.io/mxdn4U58Su-UQXwzOHpHag (Analysis of
rustc-benchmarking-data), I noticed that it mentions
Lokathor/tinyvec#161.

The tinyvec crate, with the default set of features, can compile on
rustc < 1.55 by using large implementations of Default for [T; N].  On
Rustc >= 1.55, it can elide this code and just use const generics.
Apparently this reduces compilation time for tinyvec by about half -
which is not a lot in the context of librsvg, but let's make use of
that.

Librsvg currently requires Rust >= 1.64.0 anyway, so we can make use
of tinyvec's rustc_1_55 feature.

Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/822>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants