Repository navigation
Avoid short-lived allocations in union and intersection property handling - #64815
Open
Jake Bailey (jakebailey) wants to merge 4 commits into
Open
Jake Bailey (jakebailey) wants to merge 4 commits into
Jake Bailey (jakebailey) wants to merge 4 commits into
Conversation
Jake Bailey (jakebailey)
added this pull request to stack #64806
October 14, 2026 14:20
Jake Bailey (jakebailey)
force-pushed
the
jakebailey/mem/never-reduction-probes
branch
from
October 14, 2026 16:02
7337902 to
2473949
Compare
getReducedType runs somePropertyReducesToNever on intersections. It
used to create and cache a synthetic property for every property name
shared by more than one constituent, only to inspect its flags. With
large JSX prop intersections, as in MUI, most of those properties were
never looked up again, yet they stayed in the intersection's property
cache with their symbols and links.
Collect the constituent properties without building the combined
symbol, and only create the real property in the rare merged
instantiation case.
mui-docs:
│ before │ after │
│ memory-used-B │ memory-used-B vs base │
MUIDocs 1.857Gi ± 0% 1.717Gi ± 0% -7.55% (p=0.002 n=6)
│ before │ after │
│ allocs │ allocs vs base │
MUIDocs 27.29M ± 0% 25.19M ± 0% -7.72% (p=0.002 n=6)
│ before │ after │
│ allocated-B │ allocated-B vs base │
MUIDocs 4.730Gi ± 0% 4.556Gi ± 0% -3.68% (p=0.002 n=6)
│ before │ after │
│ symbols │ symbols vs base │
MUIDocs 4.332M ± 0% 3.663M ± 0% -15.43% (p=0.002 n=6)
Synthesizing or probing a union or intersection property gathers the
distinct constituent properties into a set. That set rarely holds more
than a handful of symbols, but collections.OrderedSet always allocates
both a map and a key slice, and this runs for every property of every
combined type that is enumerated during relation checks.
Collect them in the checker's orderedSet instead, which scans a slice
for small sets and only builds a map once the set grows large enough
for hashing to pay off.
mui-docs:
│ before │ after │
│ allocs │ allocs vs base │
MUIDocs 25.19M ± 0% 23.32M ± 1% -7.41% (p=0.002 n=6)
│ before │ after │
│ allocated-B │ allocated-B vs base │
MUIDocs 4.556Gi ± 0% 4.389Gi ± 1% -3.66% (p=0.002 n=6)
Enumerating the properties of a union or intersection tracks which
names have already been combined. A fresh set is allocated for every
enumeration, and on large projects those sets account for a sizable
share of all allocated bytes, adding GC work for no lasting benefit.
Borrow the set from a per-checker free list instead. Enumeration can
recurse into other combined types, so each call still takes its own
set.
mui-docs:
│ before │ after │
│ allocated-B │ allocated-B vs base │
MUIDocs 4.389Gi ± 1% 4.092Gi ± 0% -6.76% (p=0.002 n=6)
│ before │ after │
│ allocs │ allocs vs base │
MUIDocs 23.32M ± 1% 23.18M ± 0% -0.60% (p=0.002 n=6)
Jake Bailey (jakebailey)
removed this pull request from stack #64806
October 14, 2026 16:55
Jake Bailey (jakebailey)
changed the base branch from
jakebailey/mem/shared-property-cache
to
main
October 14, 2026 16:56
Jake Bailey (jakebailey)
force-pushed
the
jakebailey/mem/never-reduction-probes
branch
from
October 14, 2026 16:56
2473949 to
e2fcf0f
Compare
This was referenced Oct 14, 2026
Jake Bailey (jakebailey)
marked this pull request as ready for review
October 14, 2026 16:58
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Popped pool slots retain oversized name sets, defeating the intended retention limit.
1 open finding
What changed in this PR
Reduces temporary allocations in the native TypeScript checker's union and intersection property handling.
Changes:
- Checks intersection reduction directly from constituent properties.
- Uses slice-backed ordered sets for property sources.
- Pools name sets used during property enumeration.
| File | Description |
|---|---|
| tsc/internal/checker/utilities.go | Adds name-set borrowing and release helpers. |
| tsc/internal/checker/checker.go | Refactors property collection and reduction; integrates name-set pooling. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Reslicing the free list left the popped set reachable from the backing array, so a set that later grew past the retention limit and was discarded on release stayed alive until its slot was overwritten.
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Jake Bailey (jakebailey)
requested a review
from Anders Hejlsberg (ahejlsberg)
October 14, 2026 17:23
Contributor
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This branch has not been deployed
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.

Enumerating and reducing union and intersection properties does a lot of short-lived work: it synthesizes properties only to read their flags, collects constituent properties into map-backed sets, and allocates a fresh name set for every enumeration.
nevernow inspects the constituent properties directly, and only creates the real property in the rare merged-instantiation case. Previously it built and cached a synthetic property for every shared name, and on MUI's large JSX prop intersections most of those were never looked up again.orderedSet, which scans a slice for small sets instead of always allocating a map and key slice.Cumulative MUI measurements: 15.43% fewer symbols, 7.55% less retained heap, about 15% fewer allocations, and about 13.5% fewer allocated bytes. Each commit has its own benchstat table. MUI diagnostics are unchanged.