Pre-size MultiDictionary in CreateEvaluatedIncludeSnapshotIfRequested to avoid resize allocations#13377
Merged
JanProvaznik merged 1 commit intodotnet:mainfrom Mar 19, 2026
Conversation
… to avoid resize allocations
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a capacity-aware constructor to the internal MultiDictionary<K, V> class and uses it in CreateEvaluatedIncludeSnapshotIfRequested to pre-size the backing dictionary, avoiding repeated resize allocations when populating from a known-size collection.
Changes:
- Added a new
MultiDictionary(int capacity, IEqualityComparer<K> keyComparer)constructor that forwards the capacity to the backingDictionary. - Updated the
CreateEvaluatedIncludeSnapshotIfRequestedcall site to passitems.Countas the initial capacity.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Build/Collections/MultiDictionary.cs |
Added capacity-aware constructor overload |
src/Build/Instance/ProjectInstance.cs |
Pass items.Count as capacity when constructing the MultiDictionary |
JanProvaznik
approved these changes
Mar 19, 2026
This was referenced Mar 19, 2026
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.
This pull request was generated by the VS Perf Rel AI Agent. Please review this AI-generated PR with extra care! For more information, visit our wiki.
Issue: The allocation hot path ends in
Microsoft.Build.Collections.MultiDictionary<string, ProjectItemInstance>.Add. InCreateEvaluatedIncludeSnapshotIfRequested, the MultiDictionary is constructed with default capacity (new MultiDictionary<string, ProjectItemInstance>(StringComparer.OrdinalIgnoreCase)) and then populated in a foreach loop over all project items.Each time the backing Dictionary<string, SmallList> exceeds its current capacity, Dictionary.Resize allocates a new Entry[] array — producing ~log₂(N) intermediate arrays that become immediate garbage. For a project with 4000 items this is ~12 resize allocations, with progressively larger arrays that can reach LOH thresholds.
Issue type: Specify an up-front capacity for collections if one is known.
Proposed fix: Add a capacity-aware constructor overload to MultiDictionary<K, V> and pass items.Count at the call site in
CreateEvaluatedIncludeSnapshotIfRequested. The items parameter is ICollection so .Count is O(1). The capacity is an upper bound (unique key count ≤ item count), resulting in at most ~1–5% over-allocation — negligible compared to eliminating all resize allocations. MultiDictionary is an internal class; the existing constructor is preserved and the new overload is additive.This directly targets the Dictionary.Resize → Dictionary.Insert → MultiDictionary.Add → CreateEvaluatedIncludeSnapshotIfRequested frames seen in the stack trace that lead to TypeAllocated!Entry[System.String, MultiDictionary+SmallList[...]][].
Best practices wiki
See related failure in PRISM
ADO work item