Avoid allocations when canonicalizing - #161077
Conversation
There are many short lived `Canonicalizer` instances. These each involve some vecs and hashmaps, and the cost of the allocations for these adds up. This commit introduces `CanonicalizerState` which holds the vecs and hashmaps, and puts a single instance in `InferCtxt` to be cleared and reused for each canonicalizer.
It's a small performance win. This requires adding `DelayedMap::clear` (and `DelayedSet::clear` is also added for symmetry).
|
@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.
Avoid allocations when canonicalizing
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (42e1aad): comparison URL. Overall result: ❌✅ regressions and improvements - please read:Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=never rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary 6.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -0.7%, secondary 2.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis perf run didn't have relevant results for this metric. Bootstrap: 457.997s -> 456.358s (-0.36%) |
|
Some changes occurred to the core trait solver cc @rust-lang/initiative-trait-system-refactor |
|
LLM disclosure: the idea for this came from an LLM after analyzing a Cachegrind profile. I wrote the code and text myself. |
|
|
||
| fn release_canonicalizer_state(&self, mut state: CanonicalizerState<Self::Interner>) { | ||
| // Clear (don't deallocate) the state for later reuse. | ||
| state.clear(); |
There was a problem hiding this comment.
could be worth adding a debug assertion here that the cleared state == a default state. To make sure we never forget a field...
| pub fn clear(&mut self) { | ||
| // Deconstruct to ensure no fields are missed. | ||
| let Self { variables, var_kinds, variable_lookup_table, sub_root_lookup_table, cache } = | ||
| self; |
There was a problem hiding this comment.
I guess deconstructing does help as well
|
I think the deconstruction is good enough; it's a well-established idiom. Thanks! @bors r=jdonszelmann |
This comment has been minimized.
This comment has been minimized.
What is this?This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.Comparing 2fb4ed8 (parent) -> d453bdd (this PR) Test differencesShow 2 test diffs2 doctest diffs were found. These are ignored, as they are noisy. Test dashboardRun cargo run --manifest-path src/ci/citool/Cargo.toml -- \
test-dashboard d453bdd8f092d099bc336f0bda4163f809ad18e0 --output-dir test-dashboardAnd then open Job duration changes
How to interpret the job duration changes?Job durations can vary a lot, based on the actual runner instance |
Canonicalization is hot in the new solver. This commit avoids some allocations while doing it. Details in individual commits.
r? @lcnr