Skip to content

Commit 090662f

Browse files
authored
Merge pull request #1662 from joshtriplett/call-for-testing-hint-mostly-unused
Call for Testing: Speeding up compilation with `hint-mostly-unused`
2 parents 1597629 + 774a8e1 commit 090662f

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
+++
2+
path = "inside-rust/2025/07/15/call-for-testing-hint-mostly-unused"
3+
title = "Call for Testing: Speeding up compilation with `hint-mostly-unused`"
4+
authors = ["Josh Triplett"]
5+
+++
6+
7+
I'm pleased to announce, and call for testing of, the nightly-only `rustc`
8+
`-Zhint-mostly-unused` option, and the corresponding nightly Cargo features
9+
`profile.hint-mostly-unused` and `hints.mostly-unused`. These options can help
10+
accelerate your Rust compile time in some cases, by avoiding code generation
11+
for items from your dependencies that you aren't using. Your feedback will help
12+
evaluate these features and make progress towards stabilizing them in the
13+
future.
14+
15+
## Background
16+
17+
When building a Rust library crate, the compiler generates compiled code for as
18+
much of the crate as it can (everything that isn't generic and isn't marked as
19+
`#[inline]`), which gets linked into later crates into the dependency graph.
20+
However, some crates provide comprehensive APIs with a very large surface area,
21+
yet many of their users need only a few entry points. In such cases, the
22+
compiler currently spends time generating code for the entire crate, and the
23+
linker then ends up throwing most of that code away as unused.
24+
25+
This can waste a substantial amount of compile time. Some large crates can take
26+
minutes to compile, and when you use these large crates as dependencies, they
27+
can take a disproportionate amount of the entire compilation time of your
28+
overall build.
29+
30+
In some cases, crates add feature flags to control compilation of their API
31+
surface. This can improve compile time, but adds complexity for users, who now
32+
need to determine which features they need for the APIs they use. Features also
33+
constitute a stable interface of a crate, and changing feature flags can be a
34+
breaking change. And even with feature flags, not every enabled function will
35+
be needed; there is a balance between granularity and ease of use.
36+
37+
## Deferring code generation with `-Zhint-mostly-unused`
38+
39+
The latest nightly `rustc` compiler now supports an option
40+
`-Zhint-mostly-unused`, which tells `rustc` that the crate's API surface will
41+
mostly go unused. This is a hint, and `rustc` doesn't make guarantees about its
42+
exact behavior (so that we can extend or improve it in the future), but
43+
currently it causes the compiler to defer as much of code generation as
44+
possible.
45+
46+
Applying this option to key crates you depend on (and use only a small subset
47+
of) can provide a substantial reduction in compile time, for debug builds and
48+
especially for release builds.
49+
50+
## How does this perform?
51+
52+
Some build timings for clean release builds of a crate depending on various
53+
specific large API crates:
54+
55+
| **Dependency Crate** | **Before** | **`hint-mostly-unused`** | **Delta** |
56+
| :- | -: | -: | -: |
57+
| `windows`, all Graphics/UI features | 18.3s | 10.7s | -42% |
58+
| `windows`, all features | 3m 48s | 2m 55s | -23% |
59+
| `rustix`, `all-apis` feature | 5.9s | 4.3s | -27% |
60+
| `x11rb` and `x11rb-protocol` | 5.3s | 2.6s | -51% |
61+
| `aws-sdk-ec2` | 4m 07s | 2m 04s | -50% |
62+
63+
This performance improvement comes from deferring code generation. For
64+
instance, the `windows` crate in the first example goes from building in 15.1s
65+
of which 49% is codegen, to building in 7.5s of which 1% is codegen.
66+
67+
Note that this option does not provide a universal performance improvement for
68+
every crate; if used when not applicable, this option can make builds much
69+
*slower*. Deferring compilation of the items in a crate can lead to redoing
70+
code generation for those items repeatedly. In particular, this hint will
71+
probably regress compile time if applied to crates whose API surface is mostly
72+
used, and/or used in multiple different crates or binaries (e.g. multiple test
73+
binaries that each test a substantial swath of the API).
74+
75+
Always do performance analysis when considering this hint, and only apply it if
76+
it applies obvious and substantial wins for your users. Applying it across the
77+
board to all your dependencies will likely regress compilation time.
78+
79+
If most of the items in your crate are polymorphic (generic), this hint may be
80+
redundant, as Rust already defers compilation of polymorphic items until they
81+
get monomorphized with specific types.
82+
83+
Also note that this only provides a performance win if you are building the
84+
dependency. If you're only rebuilding the top-level crate, this won't help.
85+
86+
## Plumbing this through Cargo with profiles
87+
88+
In order to support compiling specific dependencies with this option, Cargo
89+
supports a [profile option
90+
`hint-mostly-unused`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-hint-mostly-unused-option)
91+
to mark a crate with this hint:
92+
93+
```toml
94+
[profile.dev.package.huge-mostly-unused-dependency]
95+
hint-mostly-unused = true
96+
97+
[profile.release.package.huge-mostly-unused-dependency]
98+
hint-mostly-unused = true
99+
```
100+
101+
Note that if you build in multiple profiles (e.g. the default dev profile and
102+
the `-r` release profile), you'll want to set this flag for both, as shown
103+
above.
104+
105+
Because this option is still nightly-only, and depends on a nightly-only
106+
`rustc` option as well, enabling it requires passing
107+
`-Zprofile-hint-mostly-unused` on the `cargo` command line. Without this
108+
option, cargo will ignore this with a warning (but not an error, as it's still
109+
just a hint). Note that as with any profile option, it only takes effect when
110+
set in the top-level crate you're building.
111+
112+
You should not, in general, set this flag for all your dependencies, or for
113+
your own crate; you should set it selectively and test to make sure it provides
114+
an improvement. Using the [cargo `--timings`
115+
option](https://doc.rust-lang.org/nightly/cargo/reference/timings.html) can
116+
help to identify crates that might benefit from this hint. And when testing
117+
this hint, `--timings` can help detect whether the build time of *other* crates
118+
in the dependency tree went up.
119+
120+
## Making this automatic: Cargo `[hints]`
121+
122+
A profile hint still requires the top-level crate to configure the hint for
123+
some of its dependencies. However, some crates know that almost all of their
124+
users will want this hint enabled. For such crates, we've introduced a new
125+
`hints` mechanism in Cargo. Unlike profiles, which only apply when set in the
126+
top-level crate you build, hints are set within individual crates in your
127+
dependency graph. Hints provide a default behavior that you can still override.
128+
129+
A crate that knows most of its users will not use most of its API surface can
130+
set this hint in its `Cargo.toml` manifest:
131+
132+
```toml
133+
[hints]
134+
mostly-unused = true
135+
```
136+
137+
Note that setting a hint does *not* increase the Minimum Supported Rust Version
138+
(MSRV) of your crate. Hints are always ignored if not understood. So, you can
139+
safely set this hint immediately, without waiting for this feature to be
140+
stabilized, and users of nightly will immediately benefit (if they pass
141+
`-Zprofile-hint-mostly-unused` to cargo to enable the feature).
142+
143+
### Future hints
144+
145+
The `hints` mechanism in Cargo is a general feature, and we plan to make use of
146+
it for other purposes in the future. For instance, we may offer a
147+
`min-opt-level` option, for crates that are so performance-sensitive (e.g.
148+
numerics code) that most users will want to build them with optimization even
149+
in development mode. As with other hints, such a mechanism would still always
150+
allow the top-level crate to override.
151+
152+
## How do I help?
153+
154+
We'd love for you to test out this feature on the latest Rust nightly compiler[^nightly].
155+
156+
[^nightly]: Make sure to run `rustup update nightly` (or however you manage your Rust releases).
157+
158+
If you maintain a crate that has a large API surface, and you expect that the
159+
typical user might use only a fraction of it, try setting `hints.mostly-unused`
160+
in your `Cargo.toml`:
161+
162+
```toml
163+
[hints]
164+
mostly-unused = true
165+
```
166+
167+
You can test the effect of this by building a *typical* crate that depends on
168+
your crate, with and without this hint set, using nightly Cargo:
169+
`cargo +nightly -Zprofile-hint-mostly-unused build -r`. If this provides a
170+
noticeable performance improvement, consider setting it in your published
171+
crate.
172+
173+
If you're building atop a crate that you only use a small fraction of, you can
174+
try setting the profile option in your own crate:
175+
176+
```toml
177+
[profile.dev.package.huge-mostly-unused-dependency]
178+
hint-mostly-unused = true
179+
180+
[profile.release.package.huge-mostly-unused-dependency]
181+
hint-mostly-unused = true
182+
```
183+
184+
Please report any performance improvements, or unexpected performance issues,
185+
or *especially* any failures you observe, to the [tracking issue for
186+
profile-hint-mostly-unused](https://github.com/rust-lang/cargo/issues/15644).
187+
When reporting, please tell us:
188+
- What hints or profile settings you added
189+
- What crates you added them to
190+
- What top-level crate you're building
191+
- What features you set when building
192+
- What build profile you're using (e.g. the default dev profile, or the release
193+
profile)
194+
- Whether you did a clean build or an incremental rebuild
195+
- What performance numbers you got with and without the option you added
196+
197+
We'll take this feedback into account to fix any issues with either the rustc
198+
compiler feature or the Cargo features, and to evaluate when those features
199+
have seen enough testing to be ready to stabilize.
200+
201+
## Acknowledgements
202+
203+
Much appreciation to:
204+
- [Ben Kimock](https://github.com/saethlin), whose work towards MIR-only rlibs
205+
provided inspiration and infrastructure for this work.
206+
- The [Rust All Hands](https://rustweek.org/all-hands/) and its organizers, for
207+
providing a forum to discuss and progress this work.

0 commit comments

Comments
 (0)