Change HashMaps and HashSets in Cargo to use Fxhasher#17169
Conversation
|
r? @epage rustbot has assigned @epage. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
I believe this would resolve #15649. On that, there was some discussion on what hasher to use. Haven't checked the implementation yet but I'm fully on board with aliases. Also, did you check our I also wonder about moving away from hashmaps of unit -> structs in compilation to tracking an index and using struct-of-arrays to just remove any of this overheadd |
|
|
||
| gctx.shell() | ||
| .print_json(&HashMap::from([("success", "true")]))?; | ||
| .print_json(&HashMap::from_iter([("success", "true")]))?; |
There was a problem hiding this comment.
Aside: would love to have easy access to a const array-map.
| use cargo_util::paths; | ||
| use cargo_util_schemas::manifest::RustVersion; | ||
| use std::collections::hash_map::{Entry, HashMap}; | ||
| use std::collections::hash_map::Entry; |
There was a problem hiding this comment.
Still using a HashMap?
There was a problem hiding this comment.
I assume it's because Entry is compatible. Feels weird from an auditing perspective to import related type from two different places.
@weihanglo, any thoughts?
There was a problem hiding this comment.
rustc_hash still re-exports the stdlib's HashMap, just with a different hasher, so it is indeed compatible. It doesn't export any Entry though, so I think that this is the only way to do it (or we can just re-export the Entry from util::data_structures 🙃).
| // Move the old token location to the new one. | ||
| if let Some(token) = toml.remove("token") { | ||
| let map = HashMap::from([("token".to_string(), token)]); | ||
| let map = std::collections::HashMap::from([("token".to_string(), token)]); |
There was a problem hiding this comment.
I assume this is needed for interop?
There was a problem hiding this comment.
Yes, the TOML implementation is not present for the FxHasher version.
Not that many of those. I converted them to That being said, I think that creating an alias for IndexMap/Set is still useful, as it would allow us to more easily change the implementation or hash in the future. |
|
I missed a bunch of hashmaps and hashsets previously, now it should be all of them. I wanted to use Clippy's |
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
Added the types to Clippy's |
|
https://github.com/rust-lang/cargo/actions/runs/28752700767/job/85254494553#step:5:713 Mind bumping patch versions for them? |
…ructures We want to use Cargo's optimized HashMap/HashSet and IndexMap/IndexSet versions with a fast hashing function instead. Allow using these types in various auxiliary crates, because we cannot currently tell Clippy to only apply the lint in the Cargo crate.
|
For future reference, I tried to depend directly on hashbrown, and also switch to foldhash, on top of this change. Neither seemed to have any additional effect on perf. |
Update cargo submodule 4 commits in 2f0e7011e0e9cb30cb772bf2ec1d69dce4dff4f3..59800466c5c41c444d264b1010b4d57e85a7117f 2026-07-05 12:10:34 +0000 to 2026-07-07 15:52:22 +0000 - Revert "feat: Stablize build-dir layout v2" (rust-lang/cargo#17187) - Use a set when checking visited workspace members (rust-lang/cargo#17180) - Stabilize `build-dir` layout v2 (rust-lang/cargo#16807) - Change HashMaps and HashSets in Cargo to use Fxhasher (rust-lang/cargo#17169)
View all comments
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:
rustc_hash::{HashMap, HashSet}(first commit)FxHashermaps/sets fromrustc_hash(second commit)Note that the benchmark results below are based on #17167.
First commit
Approximately a 1% win on wall-time, and 4.5% on instruction counts.
Second commit
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
HashMapandHashSetthat uses e.g.FxHasher, and ideally alsohashbrownwith theinline-morefeature.rustckind 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.