Avoid cloning parsed TOML manifest in ManifestErrorContext#17167
Merged
Conversation
Collaborator
|
r? @weihanglo rustbot has assigned @weihanglo. Use Why was this reviewer chosen?The reviewer was selected based on:
|
epage
reviewed
Jul 2, 2026
Member
Author
|
Makes sense. Another big part of allocations and performance is parsing the TOML manifests, which is of course a "known bottleneck" :) I wonder if there is some opportunity for zero-copy references into the original input, either for the TOML manifest itself, or at least for data derived from it, e.g. summaries. |
Contributor
|
I've wanted to improve things there.
|
epage
approved these changes
Jul 2, 2026
pull Bot
pushed a commit
to johnperez416/cargo
that referenced
this pull request
Jul 6, 2026
*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/cargo/pull/17169)* ### What does this PR try to resolve? While profiling the build performance of bors, one of the things that kind of screamed me in the performance profiles were many calls to `SipHasher`, the default hashing algorithm from the Rust standard library. I tried to see what would be the performance effect of: 1. Replacing the hottest instances of hashing (in HashMap and HashSet) that I found in the profiles by `rustc_hash::{HashMap, HashSet}` (first commit) 2. Replacing pretty much all hashmaps and hashsets in Cargo with the `FxHasher` maps/sets from `rustc_hash` (second commit) Note that the benchmark results below are based on rust-lang#17167. ## First commit ``` Benchmark 1: /projects/personal/rust/cargo/target/release/cargo test --no-run Time (mean ± σ): 315.2 ms ± 2.2 ms [User: 206.2 ms, System: 111.9 ms] Range (min … max): 312.2 ms … 318.3 ms 10 runs Benchmark 2: ./cargo-baseline test --no-run Time (mean ± σ): 319.2 ms ± 1.2 ms [User: 210.6 ms, System: 112.3 ms] Range (min … max): 317.4 ms … 320.7 ms 10 runs Summary /projects/personal/rust/cargo/target/release/cargo test --no-run ran 1.01 ± 0.01 times faster than ./cargo-baseline test --no-run # perf stat (baseline) 1 301 866 736 instructions # perf stat (new) 1 244 381 900 instructions ``` Approximately a 1% win on wall-time, and 4.5% on instruction counts. ## Second commit ``` Benchmark 1: ./cargo-perf test --no-run Time (mean ± σ): 305.5 ms ± 2.0 ms [User: 199.1 ms, System: 110.2 ms] Range (min … max): 303.8 ms … 310.6 ms 10 runs Benchmark 2: ./cargo-baseline test --no-run Time (mean ± σ): 318.7 ms ± 1.9 ms [User: 205.6 ms, System: 116.9 ms] Range (min … max): 317.1 ms … 321.7 ms 10 runs Summary ./cargo-perf test --no-run ran 1.04 ± 0.01 times faster than ./cargo-baseline test --no-run # perf stat (baseline) 1 302 028 601 instructions # perf stat (new) 1 192 872 611 instructions ``` Approximately a 4% win on wall-time and 9% on instruction counts. Similar micro-optimizations are a bit tricky, and I'd understand if you wouldn't want to land this. On one hand, the first commit has relatively few changes, but the perf. wins are much smaller than with the second commit. But the second commit has a lot of changes, and some of them are not performance-sensitive (but the wins are kind of spread throughout Cargo, so it's very difficult to estimate which ones are worth it or not). For my personal projects where I care about performance and don't care about DoS hashing attacks, I usually create an alias for a `HashMap` and `HashSet` that uses e.g. `FxHasher`, and ideally also `hashbrown` with the `inline-more` feature. `rustc` kind of does the same, and tries to use the Fx hash maps/sets everywhere to have better performance overall. I think that Cargo could do the same, but I don't know if you'd like to have different conventions about it than what I did in the second commit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR try to resolve?
I was profiling the build performance of
bors, and noticed that running Cargo on a no-op build takes >300ms, which seemed like a lot. I found a few places that could be micro-optimized, this one is the one that produced the largest performance win with the fewest code changes.I noticed that
ManifestErrorContextis being unconditionally created for each compiled unit (whether fresh or not), even though in the vast majority of cases, there are no manifest errors to be enriched. The error context clones a bunch of things. While taking a look at memory profiles using Valgrind's dhat, I noticed that a lot of time and memory allocations were spent on cloning the TOML document, which happened inside the error context.This PR changes the representation of the TOML manifest from
RctoArc, so that it can be directly used inManifestErrorContextwithout cloning the whole thing.This produced a ~6% wall-time win on the Cargo invocation on
bors:I compiled Cargo with
lto = "thin"to emulate the configuration we use in the Cargo that is distributed through Rustup.How to test and review this PR?
I suppose try to run and benchmark Cargo on a project with hundreds of deps before and after this change :)