IL: cache C# extension methods per CCU - #20256
Open
auduchinok wants to merge 2 commits into
Open
Conversation
Contributor
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
T-Gro
approved these changes
Aug 13, 2026
T-Gro
left a comment
Member
There was a problem hiding this comment.
Review: caching C# extension methods per CCU ✅
Really nice, well-scoped optimization. I built it locally (Build.cmd -c Debug, 0 warnings/0 errors) and ran Language.ExtensionMethodTests — 28/28 passing. The design is sound and the code comments do a great job explaining the why. Approving.
Why this is correct
- Local vs imported split is the right call. Only imported classes are cached (
if tcrefOfStaticClass.IsLocalRef then compute-uncached). A local static class is still gaining members while its own file is checked, so it must not be memoized — the comment nails this. - Cache lifetime is tied to
CcuData. It lives and dies with the referenced assembly's CCU, so it's naturally invalidated when the project/TcImportsis rebuilt. No stale-entry risk across incremental builds. - Shared
MethInfos are safe.MethInfofor imported IL methods is effectively immutable, and framework CCUs (e.g.System.Linq.Enumerable, a huge source of these) are shared with a consistentTcGlobalsviaFrameworkImportsCache, so reusing them across name-resolution envs and parallel file checking is fine.ConcurrentDictionary+TryGetValue/TryAddmakes concurrent computation idempotent (double-compute at worst, never corruption). - Priority is deliberately not cached.
NextExtensionMethodPriority()is still called peropenand applied outside the cachedshape, preserving the original ordering semantics. TheNone/Some (Some/None)mapping is faithfully reproduced via(TyconRef option * MethInfo). - Conservative on import failure. Marking
importFailedand skipping theTryAdd(while still returning the partial shape) means a transient import failure won't be baked in permanently — same worst-case cost as before, no regression. isApplicableis checked before touching the CCU/cache, so non-extension classes pay nothing extra.- All four
CcuDataconstruction sites are updated, and the.fsifield order matches the.fs— build confirms.
Minor / optional (non-blocking)
ConcurrentDictionary(1, 0)usesconcurrencyLevel = 1, which serializes the (rare) writes on a single lock. Reads are still lock-free, and writes happen at most once per static class, so this is fine — just flagging that the default ctor would behave near-identically if you'd rather not hardcode.- Empty-but-applicable classes now skip
NextExtensionMethodPriority()(the counter no longer advances for them). That's benign — priority is only observable when members exist — worth a mental note only. - Typing the field as
obj+:?>on read is a pragmatic way around theMethInfo-declared-later ordering, and the.fsicomment explains it well. No change needed.
The FCS (−3.16%) and FSharp.Common (−10.68%) allocation wins are excellent for essentially zero downside.
T-Gro
self-requested a review
August 13, 2026 16:10
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.
GetCSharpStyleIndexedExtensionMembersForTyconRefhad no cache, so everyopenof a static classrebuilt a
MethInfochain for each of its extension methods and re-imported their type parameters. On a57-file project with 489 references that meant 98,986
MethInfos describing only 6,782 distinctextension methods on 1,190 static classes.