Skip to content

Make Entity's adhoc members list lazy - #20286

Open
auduchinok wants to merge 4 commits into
dotnet:mainfrom
auduchinok:il-lazyEntityFields
Open

Make Entity's adhoc members list lazy#20286
auduchinok wants to merge 4 commits into
dotnet:mainfrom
auduchinok:il-lazyEntityFields

Conversation

@auduchinok

Copy link
Copy Markdown
Member

Every Entity carried two objects most entities never use.

tcaug_adhoc_listTyconAugmentation.Create allocated an empty ResizeArray per entity, and
exactly one site ever adds to it (PublishValueDefnMaybeInclCompilerGenerated). Nothing is added for an
imported type: NewILTycon closes the augmentation and publishes no ad-hoc members. It is now allocated at
that single site behind AddAdhocMember, with AdhocMembers for the two readers, and unpickling an empty
list stores nothing.

entity_il_repr_cache — a cache cell only IlxGen reads. An analysis-only host allocates one per
entity and never touches it. CompiledReprCache now creates it on first request, so the member's
signature is unchanged and no caller can observe a null.

No FCS public API change; the internal record fields become nullable, tcaug_adhoc_list becomes mutable,
and TyconAugmentation gains AddAdhocMember / AdhocMembers.

One project held open (mean of 3 fresh processes)

Project Before After Retained
consoleapp 33.84 MB 33.76 MB -0.08 MB (-0.24%)
Oxpecker 72.83 MB 72.52 MB -0.31 MB (-0.43%)
IcedTasks 50.14 MB 50.03 MB -0.11 MB (-0.23%)
FsToolkit 75.39 MB 75.23 MB -0.16 MB (-0.21%)
Fantomas.Benchmarks 64.77 MB 64.71 MB -0.06 MB (-0.09%)
Prime 108.37 MB 108.23 MB -0.14 MB (not established)
Fantomas.Core 109.50 MB 109.26 MB -0.24 MB (not established)
Fantomas.Core.Tests 141.95 MB 141.74 MB -0.21 MB (not established)
FSharp.Common 297.67 MB 296.35 MB -1.32 MB (-0.44%)
fcs 1273.30 MB 1273.31 MB +0.01 MB (not established)

A whole solution held open at once (mean of 2 passes)

Solution Before After Retained
resharper 1746.27 MB 1737.29 MB -8.98 MB (-0.51%)
fcsrepo 2557.91 MB 2555.86 MB -2.05 MB (-0.08%)
fantomas 690.18 MB 689.28 MB -0.90 MB (-0.13%)
oxpecker 254.06 MB 252.19 MB -1.87 MB (-0.74%)
fstoolkit 238.00 MB 237.82 MB -0.18 MB (not established)
icedtasks 212.48 MB 211.74 MB -0.74 MB (-0.35%)
prime 231.86 MB 231.77 MB -0.09 MB (not established)
consoleapp 33.82 MB 33.75 MB -0.07 MB (-0.22%)

What it removes

gcdump of one held FSharp.Common analysis:

before after never used
cache<CompiledTypeRepr> 26,465 (0.61 MB) 2,873 (0.07 MB) 89%
List<bool * ValRef> 26,721 (0.82 MB) 1,413 (0.04 MB) 95%

1.32 MB of objects against 1.32 MB measured. The right-hand column is the argument for the change: 95% of
augmentations never receive an ad-hoc member and 89% of entities are never asked for a compiled
representation, because this host only analyses.

…tion cache on first use

Every Entity carried two objects that most entities never use.

TyconAugmentation.Create allocated an empty ResizeArray for tcaug_adhoc_list, one
per entity, and nothing is ever added for an imported type: NewILTycon closes the
augmentation and adds no ad-hoc members. There is a single Add site, so the list is
now allocated there, behind AddAdhocMember, with AdhocMembers for the readers.
Unpickling an empty list stores nothing rather than an empty ResizeArray.

entity_il_repr_cache held a cache cell used only by IlxGen, so an analysis-only
host allocated one per entity and never read it. CompiledReprCache now creates it
on first request, leaving the member's signature unchanged so no caller sees null.

Retained memory after ParseAndCheckProject drops 0.05-1.38 MB per project across
the measurement suite, and analysis time is unchanged within noise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Aug 18, 2026

@T-Gro T-Gro 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.

The invariant that makes this safe reads cleanly: tcaug_adhoc_list is now always either null or non-empty — Create starts null, AddAdhocMember allocates on first write, unpickling an empty list stores null, and remapping maps null to null. Nothing downstream consumes the list except pickling (through AdhocMembers) and remapping; IlxGen sorts off tcaug_adhoc / MembersOfFSharpTyconSorted, never the list, so the lazily-allocated field has no hot-path reader to trip over.

Lazy CompiledReprCache is equally benign under concurrency: the field is a reference written atomically and cached was already non-atomic, so a racing first-touch costs at most one redundant, deterministic recompute — and the entities most likely to be shared across parallel-checking threads (imported ones) still allocate eagerly in u_entity_spec_data, so the window is tiny.

Built Debug clean, 0 warnings with nullness checking on. Measurement-backed and tightly scoped — lovely change.

One optional follow-up: u_entity_spec_data still does entity_il_repr_cache = newCache() for every unpickled entity, so imported types keep paying for a cache an analysis-only host never reads. Since CompiledReprCache now tolerates null, that site could be null too and reclaim the imported-entity share as well.

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Aug 19, 2026
@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Aug 19, 2026
@T-Gro
T-Gro self-requested a review August 19, 2026 09:29
@T-Gro
T-Gro enabled auto-merge (squash) August 19, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-reviewed PR reviewed by AI review council AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants