Computing crate_hash from metadata encoding instead of HIR (implements #94878) - #154724
Computing crate_hash from metadata encoding instead of HIR (implements #94878) #154724Daniel-B-Smith wants to merge 3 commits into
Conversation
This comment has been minimized.
This comment has been minimized.
24c04d5 to
c826667
Compare
This comment has been minimized.
This comment has been minimized.
c826667 to
1fe48e6
Compare
This comment has been minimized.
This comment has been minimized.
43a7704 to
338aba3
Compare
This comment has been minimized.
This comment has been minimized.
0ddcd96 to
d27cca5
Compare
This comment has been minimized.
This comment has been minimized.
1e5f269 to
4171895
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
4171895 to
bb57d62
Compare
This comment has been minimized.
This comment has been minimized.
bb57d62 to
1ad9d87
Compare
This comment has been minimized.
This comment has been minimized.
|
I've removed the "do not submit" because I have now tested 100+ commits with tokio, ripgrep, bstr, zstd, serde_json, and parking_lot. No feedback on Zulip on the XXH3 vs SipHash question, so I do want to flag that here. I don't see any difference in correctness between the two hashes, but XXH3 is dramatically faster. |
|
Please remove the auto inserted LLM tool advertisement from the commit |
This comment has been minimized.
This comment has been minimized.
|
Removed the LLM tag from the XXH3 commit. I left it there to err on the side of disclosure, but happy to remove it. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| .local_crate_hash | ||
| .get() | ||
| .expect("crate_hash(LOCAL_CRATE) called before metadata encoding") | ||
| } else { |
There was a problem hiding this comment.
Leave a comment to that regard
- on the first arm, stating that we don't need to compute it because we already did so for metadata encoding
- on the second arm that were doing the same HIR based crate hash computation that metadata encoding is doing
|
Reminder, once the PR becomes ready for a review, use |
|
This PR changes how LLVM is built. Consider updating src/bootstrap/download-ci-llvm-stamp.
This PR modifies If appropriate, please update |
This comment has been minimized.
This comment has been minimized.
The SVH is computed by hashing the entire encoded metadata blob, and SipHash-128 (~2.5 Ir/byte) dominated the cost — it accounted for the whole of the measured regression on metadata-heavy crates. Switch the byte pass to XXH3-128, which is ~10x cheaper per byte on its runtime-detected AVX2 path. XXH3 is non-cryptographic, which is fine here: the SVH is only a change detector, must be reproducible (so it is never keyed secretly, meaning even the previous SipHash use was not collision-resistant), and compiling a crate already executes its build scripts and proc-macros, so the crate author is trusted regardless. The untracked supplements (dep-tracking hash and the HIR hash for e.g. DefKind::GlobalAsm) are still folded in via a StableHasher fingerprint, so coverage is unchanged. twox-hash 2.x is already in the dependency graph via ruzstd; this only adds a direct edge (xxhash3_128 + std features for the runtime SIMD dispatch).
|
This PR was rebased onto a different main 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. |
|
@rustbot ready |
|
the LLVM thing was a bad rebase that has been fixed. I've also double checked that the disable flag still works. |
|
@bors r+ rollup=never |
|
⌛ Testing commit e7d9d02 with merge 1ab92c7... Workflow: https://github.com/rust-lang/rust/actions/runs/32012882448 |
| #[derive(MetadataEncodable, BlobDecodable)] | ||
| pub(crate) struct CrateHeader { | ||
| pub(crate) triple: TargetTuple, | ||
| pub(crate) hash: Svh, |
There was a problem hiding this comment.
Um, I'm pretty sure removing this field will reintroduce the problem of #76720. Putting the crate hash after the unhashed data does not work as that makes the offset of the crate hash differ between rustc versions.
There was a problem hiding this comment.
That issue was fixed by disabling incremental across stages #111982. That is still the case in main:
rust/src/bootstrap/src/core/builder/cargo.rs
Line 1290 in 6d656b1
Even if that weren't the case, I'm pretty sure this would be fine because the version mismatch will error out before anything can try to decode the crate hash. That check is here:
rust/compiler/rustc_metadata/src/locator.rs
Line 908 in 6d656b1
get_crate_hash().
Just to be sure, I followed the reproduction instructions and everything worked fine. Specifically:
git checkout 34baba5394fcbda4cba7b7c19 # Base for my branch
./x clean
./x build
git checkout e7d9d02a6311e5cdf09dbcee73ac5 # My branch at the time of this comment
./x build
./x test
Everything succeeded.
There was a problem hiding this comment.
That issue was fixed by disabling incremental across stages #111982.
According to the PR description that PR fixes the problem when combined with #111329 which this PR breaks. I don't know if #111329 is actually necessary though.
I'm pretty sure this would be fine because the version mismatch will error out before anything can try to decode the crate hash.
The version stays constant when you make local modifications.
Just to be sure, I followed the reproduction instructions and everything worked fine.
According to #111329 (comment) both a change to the metadata format (without bumping METADATA_VERSION like this PR does rather than keeping it the same like everything that doesn't touch the metadata header does) and a dependency version bump have to be made at the same time to trigger the bug.
That said, I just noticed that when I said
Putting the crate hash after the unhashed data does not work as that makes the offset of the crate hash differ between rustc versions.
this was incorrect. I misread the UNHASHED_POS_* as being the CrateRootUnhashed rather than just a u64 indicating the position of it. A u64 is fine, so I'm retracting my r-. My apologies for misreading the code.
|
@bors r- |
|
This pull request was unapproved. Auto build was cancelled due to unapproval. Cancelled workflows: |
|
@rustbot ready |
|
Error: Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #triagebot on Zulip. |
| #[derive(MetadataEncodable, LazyDecodable)] | ||
| pub(crate) struct CrateRootUnhashed { | ||
| extra_filename: String, | ||
| } |
There was a problem hiding this comment.
Why is this actually unhashed? This is stored in the hashed part of the metadata of dependent crates, right?
There was a problem hiding this comment.
It is not hashed currently. #86045 removed this from the hash.
If this field is included in the hash, https://github.com/rust-lang/rust/blob/6d656b1efca82491a110b476ac9bbe712628ccb6/tests/run-make/emit-path-unhashed/rmake.rs fails specifically.
There was a problem hiding this comment.
Was the extra_filename for dependencies previously covered by the crate hash? It almost certainly is with this PR.
There was a problem hiding this comment.
Good point. It certainly was not. I've moved that field to the unhashed portion and added a test.
I put that into a separate commit for review, but it's probably worth squashing before merging.
View all comments
This PR converts the crate_hash/SVH to depend on metadata instead of HIR whenever metadata is needed. A trimmed down crate_hash is kept for dylib/binary cases where metadata is not present. It is believed that the metadata will be a more sound way to track dependency changes.
Related to #94878
The metadata hash is calculated on the raw bytes before flush because hashing the elements incrementally turned out to be too expensive.
The change to the HIR hash is potentially safe even without the metadata crate_hash change. Without that change, this PR is a performance regression. I am bundling them because I believe the HIR hash change is more likely to be safe in light of the HIR hash being less load bearing on the SVH.
The dylib/binary crate_hash removes components that I believe are not relevant to those cases. Analysis:
The comment says: "Hash visibility information since it does not appear in HIR." Visibilities only matter to external users of the crate (i.e. consumers of metadata). Without metadata, visibility differences are not observable, so this contributes nothing for incremental correctness.
The comment says: "that content is exported into crate metadata, so any changes to it need to be reflected in the crate hash." Without metadata, there is nothing to export, so this is purely metadata-motivated. The visualizer file path is already covered by HIR (the attribute) and the content is loaded later only if metadata is being written.
These exist solely so that the crate-hash reflects remapped source paths that get embedded into metadata (the comment explicitly says "If we included the full mapping in the SVH, we could only have reproducible builds…"). The remapping itself is already captured via dep_tracking_hash, so for incremental this is redundant.
The crate hash calculation can be reverted via
-Z metadata-crate-hash=no. I manually confirmed that setting that flag generates the same hash as the commit prior to my change. The test for that flag only checks that it changes the value of the hash and does not check the specific value of the hash. I could add a check for the latter, but I'm nervous that a hash golden test would be a maintenance headache.