-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add "Optimizing Build Performance" section to the Cargo book #15924
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
Conversation
Co-authored-by: Nicholas Nethercote <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for writing this draft!
Not sure if its too obvious but we might mention that removing unnecessary crates from your dependency tree will help reduce compilation time. |
We'll be pulling content from https://corrode.dev/blog/tips-for-faster-rust-compile-times/ (linked in the zulip thread) which includes |
Interesting, your suggestions are more like a "directive" - i.e. "we recommend you to do exactly this to get these benefits", where I tried to talk more broadly about the context of what is being changed. I can't really say what is better! But I do get the concern about duplicating content described elsewhere. I integrated your PR with some additions. Maybe we should spend some time explaining what's the difference between configuring something in I added "✅ Smaller disk usage of the target directory" to the trade-off section for the debuginfo config. I'm not sure if it "belongs" in this guide, but it was something mentioned by a lot of people as a sort of a blocker in the compiler performance survey. Even though it doesn't sound immediately relevant to build performance, it can actually affect it a lot. I know from my experience (with a very limited disk space), that the large size of Btw, maybe we should also add a separate guide/page for "Optimizing Runtime Performance" later? :) |
What I would like to convey is "here is a starting point that will likely work for most people" and then give context to help decide whether its worth applying at all or how to tweak it. My concern with first principles is too many people won't care and just want something to try and we'll lose them with the discussion.
Its nor covered elsewhere. If you can find a good wait to integrate a break down on on when to use any of the options (manifest, repo config, user config), then that'd be great!
Performance isn't just speed, so I'm fine with it so long as our calling it out is proportionate to the effect.
We're recommending this for the |
I tried to add a section about the various ways of how to config things, but I almost wonder if it shouldn't be a separate page. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have some "tips" at https://doc.rust-lang.org/nightly/cargo/reference/timings.html, should we switch that to a link to this page?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe fold that page into this one and setup a redirect
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to include a "how to profile my builds" in this page, which could link to the timings page as one of the points. And the timings page could then cross-link here for "how to optimize build perf.".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was considering bringing up dividing this into two sections
- Root causing build times (
--timings
, compiler passes, macro lines,cargo llvm-lines
) - Optimizing builds
I was just split on that vs framing those in terms of the current organization. For example, we could have a section on reducing the impact of generics and talk through using cargo llvm-lines
to find large, repeated generic functions and discuss how to improve that.
|
||
## Where to apply configuration changes | ||
|
||
You can apply the configuration changes described above in several places: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to be careful here because a lot of users are confused about what can go in Cargo.toml
and what can go in .cargo/config.toml
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now it says
You can set the described options either in the
Cargo.toml
manifest, which will make them available for all developers who work on the given crate/project, or in theconfig.toml
configuration file, where you can apply them only for you or even globally for all your local projects.
Depending on how this evolves, not everything will be supported in both locations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can iterate further on this as we add more content
|
||
Since this is currently an unstable option, you will also need to either pass `-Z codegen-backend` to Cargo, or enable this unstable option in the `.cargo/config.toml` file. You can find more information about the unstable `codegen-backend` profile option [here](../reference/unstable.md#codegen-backend). | ||
|
||
Note that the Cranelift backend might not support all features used by your crate. It is also available only for a limited set of targets. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is also available only for a limited set of targets.
That might be an issue without #4897
It seems like you're aiming at a more broader vision for this guide, which describes not only changing Cargo options, but pretty much anything that can be done to improve build performance (including changes to crate organization and source code patterns). That seems far enough from my original version that I'm not sure if this PR still makes sense in its current shape. Maybe I could change the intro back to match Ed's PR, and then we merge only the debuginfo section, and then we expand the page incrementally section by section? Or do you want to only merge it once it will be "complete enough"? |
I feel like both extremes have their trade offs. If we only have one section at the time of the beta branch, that feels half baked. If we wait until its "complete enough", we may delay merging indefinitely. Some ideas we can do we mixture of
btw If we don't have a single home for this guide, I would very much consider crate organization to be relevant to the Cargo guide because a lot of the design input and trade offs are cargo related. The main one language related I can think of is orphan rules. |
I also thought that merging this after beta branches might be a reasonable way of giving us enough time to build the content incrementally before the next stable release. I split the text into two parts (Cargo configuration and source code organization) and rewrote the Cranelift section in the "trade-off format". |
…ranelift backend trade-offs, remove TODO
Reworded the intro, removed nightly/unstable feature sub-headings, clarified Cranelift trade-offs, and removed the TODO. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the work! The plan is to merge this after beta branch (the 12th) and continue iterating from there with the hope we have enough content by the next beta branch to justify this section
Trade-offs: | ||
- ✅ Faster code generation (`cargo build`) | ||
- ❌ **Requires using nightly Rust and an unstable Cargo feature** | ||
- ❌ Worse runtime performance of the generated code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth mentioning in some way that you can mix codegen backends, by using llvm for dependencies which are often more performance critical and cranelift only for your own crate.
Example from bevy subsecond plugin recommendations
[profile.dev]
codegen-backend = "cranelift"
debug = false
# Consider compiling deps with cranelift if you want cold-compilation to be faster
[profile.dev.package."*"]
codegen-backend = "llvm"
Another facet of runtime performance: In my experience, cranelift is only slower by a very negligible amount with opt-level=3 vs opt-level=1, which also helps alleviate runtime performance issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like we do for debug
, should we just switch the example to get good performance with fast incremental builds, at the cost of full builds?
|
||
Cargo uses configuration defaults that try to balance several aspects, including debuggability, runtime performance, build performance, binary size and others. This section describes several approaches for changing these defaults that should be designed to maximize build performance. | ||
|
||
You can set the described options either in the [`Cargo.toml` manifest](../reference/profiles.md), which will make them available for all developers who work on the given crate/project, or in the [`config.toml` configuration file](../reference/config.md), where you can apply them only for you or even globally for all your local projects. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is misleading:
./.cargo/config.toml
can be included in a projects version control, and therefore used to set configs for all contributors. Its not inherently only for local/personal changes.- Not all config options are allowed in both
Cargo.toml
and.cargo/config.toml
. This is quite an important distinction: As someone active in the Bevy community, where compile time optimizations have been recommended for a long time, I've repeatedly had to explain to people that these are different files. People often skipped reading the part of Bevys setup guide which explains the distinction and went straight to copying things, which made their builds fail. Even if every option mentioned on this page can be applied to both, I still consider it important to ensure a section like this doesn't lead to confusion down the line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your second point is also raised at #15924 (comment). I figured we could wait and evolve as this continues.
As for the first, that is true, but since all we are talking about is profile so far, there is no point in putting that in a committed .cargo/config.toml
See also #15924 (comment)
I like the direction of this, an official resource for a set of reasonably effective and minimally invasive compile time optimizations is very welcome. If i understand correctly, more content is planned for follow-up PRs, but as I don't see a tracking issue or similar i'll leave my thoughts here. These are the config-only optimizations i've had the best experience with, and would like to see mentioned:
|
@laundmo what we plan to include was mentioned on zulip and at #15924 (comment) but that can be harder to find, so I edited the PR description with it. An overall challenge we'll have over time is deciding what content is worth including, including
For myself, with 1.90 released, is there enough of a gain over defaults to suggest people to go down this route?
Not on the blog post but worth considering.
Not on the blog post but I believe @Kobzol mentioned this one at some point |
There are diminishing returns going from lld -> mold -> wild, but for very large binaries or binaries with a lot of debuginfo, the difference can still probably be in the order of a few seconds. But perhaps more importantly, most targets will still be using the system linker, we're only switching the x64 Linux target to LLD in 1.90. |
I actually had seen that comment about the corrode article, but i didn't understand it was more than a source of inspiration.
Without knowing any established rules of writing official docs, I'd consider a "multiple primary sources" kind of approach. But maybe actually testing everything is on the table, in which case I would like if platform differences could be included.
IMO, these go hand in hand: things like config-only changes probably deserve to be included even if they only improve things by a few percent, while far more complex things such as dynamic linking dependencies should probably need a useful and achieveable path forward to be worth more than a mention. When I'm talking to people about optimizeling their compile times, I'm hesitant to show them an article like the corrode.dev one. In those cases, I wish there was a resource with optimisations which are achievable without tons of effort. I don't know if this would fit that, but i think its an angle worth considering.
IMO yes. Its on the "very easy to try" side, where even small gains are worth the, in most cases, relatively small effort. |
The beta PR has been opened: rust-lang/rust#146545 so I think that we can continue here. |
Update cargo submodule 19 commits in 24bb93c388fb8c211a37986539f24a819dc669d3..966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 2025-09-10 23:16:07 +0000 to 2025-09-16 17:24:45 +0000 - fix(frontmatter): Improve error quality (rust-lang/cargo#15972) - fix: wrong variable name in documentation (rust-lang/cargo#15968) - Add "Optimizing Build Performance" section to the Cargo book (rust-lang/cargo#15924) - Remove extra apostrophe in environment-variables.md (rust-lang/cargo#15963) - Clarify warning for using `features` or `default-features` in `patch` (rust-lang/cargo#15953) - fix(frontmatter): Try alternative len code fences (rust-lang/cargo#15952) - feat(cli): Allow completions for third-party subcommand names (rust-lang/cargo#15961) - docs(index): Clarify what we mean by omitting features (rust-lang/cargo#15957) - fix(future): Report all content as a single Report (rust-lang/cargo#15943) - fix(complete): Show local crates/features over other members (rust-lang/cargo#15956) - docs(resolver): Describe the role of the lockfile (rust-lang/cargo#15958) - chore: Skip check-version-bump ci job in forks (rust-lang/cargo#15959) - Eliminate the last three "did you mean" warning phrasings (rust-lang/cargo#15356) - fix(info): Suggest a more universal `cargo tree` command (rust-lang/cargo#15954) - feat(cli): Use ellipses when truncating progress (rust-lang/cargo#15955) - feat(completer): Added completion for `--features` flag (rust-lang/cargo#15309) - fix(publish): Switch the 'ctrl-c on wait' line to a help message (rust-lang/cargo#15942) - docs: move docs building process to contributor guide (rust-lang/cargo#15854) - fix(manifest): Show error source to users (rust-lang/cargo#15939) r? ghost
This extends the build performance guide in the Cargo book with the parallel frontend. This is the first mechanism we have in the guide that is not configured via profiles (unless we want to advertise setting `rustflags` through profiles?), but rather through `RUSTFLAGS`. Which means that we have to explain how to do that. I proposed using footnotes for this, so that we can reuse them also for other thing (such as explaining how to change a profile). An alternative would be to have a short paragraph at the beginning of the configuration subsection where we explain all the possible ways of configuring things, and then we refer to them. This is a follow up to rust-lang#15924 r? @epage
What does this PR try to resolve?
This PR introduces a new section into the Cargo book, which teaches Rust users how to optimize the build performance of crates. The motivation comes from the results of the Compiler performance survey, where people expressed interest in having an official guide for improving compilation times.
This has been discussed on Zulip briefly.
How to test and review this PR?
This is not expected to be complete to avoid a very large PR that gets blocked on the details over every part. Instead this will be merged after beta branch with the expectation that it will be sufficiently filled out by the next beta branch. Initially, we'll be focusing on content from https://corrode.dev/blog/tips-for-faster-rust-compile-times/.
Rendered