IL: intern the attributes and type references read from metadata - #20486
Open
auduchinok wants to merge 2 commits into
Open
IL: intern the attributes and type references read from metadata#20486auduchinok wants to merge 2 commits into
auduchinok wants to merge 2 commits into
Conversation
A metadata reader dedups a row within its own assembly, but the same value is read again in every assembly that mentions it, and readers are shared process-wide. Reading the 489 references of a ReSharper F# plugin project produces 22983 custom attributes that are 2896 distinct values, served by 338 distinct constructors, and 9350 type references that are 1186 distinct. Three capped tables in ilread keep one instance of each across readers. They are cleared from ClearAllILModuleReaderCache, so FSharpChecker.ClearCaches reaches them: an entry outlives the reader that produced it. Interning past the cap is skipped rather than evicting, since an unshared value is correct, just larger. The values pin nothing: a blob is copied out of the metadata view, so an interned attribute keeps neither the mapping nor its reader alive. Attribute constructors are interned inside the cached seekReadCustomAttrType rather than at the use site, so a constructor is already shared by the time the attribute carrying it is interned, and attributes then compare their constructor by reference instead of walking its method ref, argument types and assembly ref. fsc gets one call per constructor row from that cache; FCS disables it through reduceMemoryUsage, so there the interning runs per attribute row. Comparing attributes structurally instead cost small projects about 4.5% of their check time. Retained memory, under editor options. Diagnostics identical with the change off and on: consoleapp 1 proj 29.2 -> 28.0 MB -1.2 (-4.2%) Oxpecker 16 proj 130.9 -> 126.7 MB -4.3 (-3.3%) Prime 5 proj 105.5 -> 103.4 MB -2.1 (-1.9%) FsToolkit 11 proj 161.3 -> 158.4 MB -2.9 (-1.8%) IcedTasks 7 proj 96.6 -> 94.9 MB -1.7 (-1.7%) ReSharper.FSharp 10 proj 385.1 -> 378.4 MB -6.7 (-1.7%) Fantomas 8 proj 349.6 -> 346.8 MB -2.8 (-0.8%) FCS solution 14 proj 878.1 -> 876.4 MB -1.8 (-0.2%) Attributes carry two thirds to four fifths of that. Interning type references alone is worth -0.3 to -1.3 MB, and the two halves sum to the whole within 0.3 MB on every subject. Warm time is unchanged. On ReSharper.FSharp the timed region profiles at 27188 ms per iteration against 27186 and 27193 ms for two runs of the baseline, and the interning adds one frame costing 7.9 ms. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
T-Gro
reviewed
Sep 9, 2026
T-Gro
self-requested a review
September 9, 2026 09:43
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
|
🔍 Tooling Safety Check — Affects-Bootstrap, Affects-Compiler-Output
|
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.
A metadata reader dedups a row within its own assembly, but the same value is read again in every
assembly that mentions it, and readers are shared process-wide. Reading the 489 references of a
ReSharper F# plugin project:
ilreadILAttribute(constructor + blob)ILMethodSpecILTypeRef(TypeRef table)Each occurrence rebuilt the chain behind one attribute: constructor, argument types, type reference,
scope reference, assembly reference, public key. Three capped tables in
ilreadnow keep one instanceof each across readers.
ClearAllILModuleReaderCache, soFSharpChecker.ClearCaches()reaches them: an entryoutlives the reader that produced it.
Cache<_, _>from illib does not fit: noClear, and adding one means touching the evictionmachinery shared with the type-subsumption and overload-resolution caches.
seekReadCustomAttrType, not at the use site, so aconstructor chain is hashed once per row per assembly instead of once per attribute; attributes then
compare their constructor by reference. Without both, small projects paid ~4.5% of their check time.
Retained memory
Whole solution checked and held at once, checker options as an editor passes them
(
keepAllBackgroundResolutionsandkeepAllBackgroundSymbolUsesoff).The saving is a roughly fixed 1-7 MB per configuration rather than a share of the heap, so it reads
largest where the heap is smallest.