Verify disk-cached values with non-local keys in per-kind batches - #111
Draft
xmakro wants to merge 1 commit into
Draft
Verify disk-cached values with non-local keys in per-kind batches#111xmakro wants to merge 1 commit into
xmakro wants to merge 1 commit into
Conversation
xmakro
force-pushed
the
perf/verify-ich-local-sampling
branch
from
August 10, 2026 16:08
b1b8f31 to
e007789
Compare
xmakro
force-pushed
the
perf/verify-ich-local-sampling
branch
from
August 10, 2026 18:01
e007789 to
bdd6c96
Compare
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.
Selects disk-cached values for incremental verification so that the selection is bit-identical between two different compiler builds: values keyed by a local
DefPathHashkeep per-key fingerprint sampling, and all other values rotate in per-kind batches slotted by a hash of the kind's label. This document is written as a complete handoff; it records the investigation that led here, every design iteration with measurements, and the methodology to reproduce them.Origin and problem statement
Context: rustc-perf PR 2510 stops passing
-Zincremental-verify-ich, relying on rust#160130 which changed the 1-in-32 verification sampling to select by key fingerprint and session count instead of by value fingerprint. Kobzol asked whether key fingerprints can depend on values from the compilation session. Investigating that question produced the following verified facts:to_smaller_hash(key_fingerprint) % 32 == session_count % 32inshould_verify_loaded_value(rustc_query_impl/src/execution.rs), called from the green-load path and the promote path (plumbing.rs). Verification itself still re-hashes the value and compares against the stored value fingerprint.(kind, key_fingerprint)(SerializedDepGraph::node_to_index_opt), so any instability there would break green marking itself. Confirmed empirically: identical sessions re-run produce byte-identical selection logs.RUSTC_FORCE_RUSTC_VERSION(rustc-fake setsrustc-perf): in a cross-compiler experiment one compiler's loaded fingerprint set was an exact subset of the other's with zero selection disagreements. Changing the forced version reshuffles 17868 of 19821 load events (all localDefPathHashkeys shift with the localStableCrateId).StableCrateIdat artifact build time with the artifact's real version string; bootstrap setsRUSTC_FORCE_RUSTC_VERSIONonly for compiletest, and the benchmark-time pin cannot apply retroactively. Master and try artifacts have different version strings, so any key naming a sysroot def has a different fingerprint on the two sides of a perf comparison. Measured by rebuilding std with a different forced stamp: 409 of 2963 distinct loaded keys shift (19 foreign-def keys across core and std, 390 opaque keys out of 1291), moving the verified subset (overlap 82 of ~93) and verified events 564 to 574.DefPathHashlow halves chain from the crate root whose seed isHash64::new(stable_crate_id.as_u64())(rustc_hir/src/definitions.rs, root hash andcompute_stable_hash), confirmed empirically (0 of 19 foreign low halves survive a std restamp). So sampling foreign keys by the low half re-introduces the noise. Opaque fingerprints are blended hashes and cannot reveal whether foreign defs contributed; in the toy experiment 390 of 1291 opaque keys were std-dependent, the rest local-only, but the selection function cannot tell them apart without walking the actual key.Design iterations and measurements
All numbers are instructions:u of an incremental-unchanged rebuild (session 2), measured with an instrumented stage1 that selects the policy via a
VERIFY_POLICYenv var, so all policies run on one binary. Baseline is the current upstream sampling.items_of_instance/symbol_name/size_estimate(instance-keyed, small values). Const-eval and large-allocation stress (ctfe-stress-5, include-blob, tuple-stress) measure ~0: large values do not flow through this load path.session_count % 32 == 0: steady state ~0, but a fixed slot is a lose-lose: an early slot makes every fresh cache lineage pay at the start, a late slot is never reached by short-lived caches so the class is never covered.stable_crate_idquery per load costs +0.12..0.14% on bitmaps (61240 loads, the most load-dense benchmark found); viakey_fingerprint_stylevtable read about the same; computing the kind-label hash for every load is worst, +0.35%.StableCrateId, cached in a newDepGraphData::local_stable_crate_id: OnceLock<u64>. Local keys sample per-key as today; everything else batches per kind atFxHash(dep_kind_label) % 32 == session_count % 32. Cost: bitmaps +0.060%, deeply-nested-multi +0.017%, everything else noise.Per-kind slots resolve the fixed-slot dilemma because they reproduce the coverage profile of today's per-key rotation exactly: slots spread over all 32 sessions, so a lineage of length L covers about L/32 of the class, same as today, with the rotation input being a build-independent string instead of an unpinned hash. Unit-keyed kinds hold a single value, so per-kind equals per-key for them.
HirId-keyed values take the batch path too (their high half is the owner's local hash, which does not match the local crate id); noHirId-keyed disk-cached query was observed in any experiment. Failure reproduction on retry is preserved: a failed session does not commit a graph, sosession_countis unchanged on retry.What the change touches
rustc_middle/src/dep_graph/dep_node.rs: addsdep_kind_labeltodefine_dep_nodes!(kind label as&'static str; unlike the discriminant, stable across builds).rustc_middle/src/dep_graph/mod.rs: re-exports it.rustc_middle/src/dep_graph/graph.rs:DepGraphDatagains the lazily cached localStableCrateIdand an accessor.rustc_query_impl/src/execution.rs:should_verify_loaded_valuetakes&DepNode, early-returns on-Zincremental-verify-ich(unchanged semantics; the flag still forces verification of every loaded value, verified at 445.7M vs 396.4M on the toy crate), then branches on the high-half comparison.rustc_query_impl/src/plumbing.rs: call-site adjustment.Reproduction methodology
rustc -CincrementalunderRUSTC_FORCE_RUSTC_VERSION=rustc-perfandRUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER=rustc-perf; snapshot the incr dir after session 1 and restore it before each measured session-2 run so every measurement is the same session. Selection was logged with an env-gated eprintln of (fingerprint, kind, session, decision); sets compared across runs/compilers/std-stamps.collector/compile-benchmarks/*; full build to populate, snapshottarget/, restore +touch src/*.rs+perf stat -e instructions:u cargo buildper policy, so every measured run is session 2 at the same slot. Caveats: absolute counts are only comparable within one compiler binary (rebuilding rustc shifts baselines); the snapshot/restore protocol inflates some baselines with policy-independent codegen work (diesel), which cancels in policy deltas; bitmaps is the extreme for classification overhead because of its load density.RUSTC_FORCE_RUSTC_VERSION=<other>(touch library/std,core lib.rs to force), which restamps sysrootStableCrateIds the way two CI artifacts differ.Alternatives considered and rejected
DefPathHashseeding from the version-carryingStableCrateId(version-independent hashing id, version kept only for v0 symbol mangling): the principled complete fix, but a cross-cutting rustc change (second crate id,def_path_hash_to_def_idmapping,TypeId/debuginfo equality across toolchains needs review).-Zincremental-verify-ich=neverfor rustc-perf: trivially identical but stops measuring verification work users pay, contradicting the measure-what-users-run goal of rustc-perf PR 2510.State and next steps
perf/verify-ich-local-sampling(this PR), base branchperf/verify-sampling-baseat 84b36a7 (2026-08-06 rollup, contains rust#160130). Single title-only commit.