Skip to content

perf: Provide ThinMap, a map that uses ThinVec when the length is small - #23228

Merged
ChayimFriedman2 merged 2 commits into
rust-lang:masterfrom
ChayimFriedman2:shrink-def-map
Nov 1, 2026
Merged

ChayimFriedman2 merged 2 commits into
rust-lang:masterfrom
ChayimFriedman2:shrink-def-map

Conversation

@ChayimFriedman2

@ChayimFriedman2 ChayimFriedman2 commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

It saves memory by being only one machine word inline and compact (no extra capacity) outline, while also being even faster if the length is commonly small because linear search is faster for small lengths.

When measuring analysis-stats, this saves 40mb on self, 77mb on buck2 and 142mb on omicron. It's also faster: 3ginstructions saved on self and buck2, and 30ginstructions on omicron (walltime is too noisy). The memory effect is also likely to appear in IDE scenarios almost as well, because a major part is def maps, and I speculate that unnamed consts also play a role (because the amount of almost-empty expression stores seem too high for real bodies).

Not all maps were changed to use this, only those that measurements show are commonly small. The threshold was at least ~95% of the maps of this kind fitting into the thin schema (that is, have at most 10 elements).

I also switched some IndexMaps to HashMaps where I believe the order does not matter.

I made ThinMap generic over the map type to support both HashMap and IndexMap. However at the end the number of IndexMaps was very small. I still can support both, but I wonder if I should or just make it non-generic and simplify the code. Currently I left it neither supporting IndexMaps nor removing the trait; will appreciate opinions.

ThinMap (and ThinSet) could very well be in their own crate, I just found no suitable crate on crates.io (also, the API isn't polished enough for an external crate).

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 25, 2026
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@Veykril Veykril left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally on board with this, though I haven't really checked all the uses it replaces. The replacement from a cursory glance seems a bit aggressive though.

View changes since this review

// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
expr_map: FxHashMap<ExprSource, ExprOrPatIdPacked>,
expr_map: ThinFxHashMap<ExprSource, ExprOrPatIdPacked>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect this one to contain quite a lot of elements on average?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not because of anon consts. I do not know for sure but I did only replace when I have data to back it.


types_map_back: ArenaMap<TypeRefId, TypeSource>,
types_map: FxHashMap<TypeSource, TypeRefId>,
types_map: ThinFxHashMap<TypeSource, TypeRefId>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise

@@ -119,15 +118,15 @@ struct ExpressionOnlyStore {
labels: Arena<Label>,

@Veykril Veykril Oct 12, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A related change I've been wanting to look into has been splitting Arena into a "live" and "finalized" Arena (backed by Vec or Box<[T]> respectively), as almost all if not all arena uses we have are actually never touched again after construction

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I investigated in the past and it didn't save much memory (HashMap is a lot more bytes on the stack than 1 usize) and it's also much more complicated because it needs a builder. You closed that issue after that.

@rustbot

This comment has been minimized.

It saves memory by being only one machine word inline and compact (no extra capacity) outline, while also being even faster if the length is commonly small because linear search is faster for small lengths.

When measuring `analysis-stats`, this saves 40mb on self, 77mb on buck2 and 142mb on omicron. It's also *faster*:  3ginstructions saved on self and buck2, and 30ginstructions on omicron (walltime is too noisy). The memory effect is also likely to appear in IDE scenarios almost as well, because a major part is def maps, and I speculate that unnamed consts also play a role (because the amount of almost-empty expression stores seem too high for real bodies).

Not all maps were changed to use this, only those that measurements show are commonly small. The threshold was at least ~95% of the maps of this kind fitting into the thin schema (that is, have at most 10 elements).

I also switched some `IndexMap`s to `HashMap`s where I believe the order does not matter.
@rustbot

rustbot commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

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.

@ChayimFriedman2
ChayimFriedman2 added this pull request to the merge queue Nov 1, 2026
Merged via the queue into rust-lang:master with commit b98172d Nov 1, 2026
20 checks passed
@ChayimFriedman2
ChayimFriedman2 deleted the shrink-def-map branch November 1, 2026 18:02
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants