From 09951ccd989348bba6e97de492ca4334dfc05506 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:00:00 -0700 Subject: [PATCH] Use paged link stores for all symbol links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symbol links were stored in Go maps keyed by symbol pointer, so every link lookup paid for a hash and probe. Map lookups for symbol links were among the hottest leaf costs when checking large projects. The paged store that already backs value symbol links indexes by the dense symbol ID instead, which is a couple of loads. Using it for the remaining symbol link kinds removes those map lookups, at the cost of under 1% more heap for the page tables. --checkers 4, n=8, default GC settings: │ before │ after │ │ sec/op │ sec/op vs base │ Tsc/bluesky 2.826 ± 2% 2.799 ± 2% -0.97% (p=0.015 n=8) Tsc/sentry 11.13 ± 3% 10.86 ± 2% ~ (p=0.083 n=8) Tsc/t3code-server 13.97 ± 2% 13.89 ± 6% ~ (p=0.645 n=8) geomean 7.603 7.502 -1.32% │ before │ after │ │ rss-B │ rss-B vs base │ Tsc/bluesky 1.201Gi ± 2% 1.203Gi ± 2% ~ (p=0.878 n=8) Tsc/sentry 3.647Gi ± 9% 3.535Gi ± 6% ~ (p=0.328 n=8) Tsc/t3code-server 2.883Gi ± 4% 2.877Gi ± 3% ~ (p=0.574 n=8) geomean 2.328Gi 2.304Gi -1.05% │ before │ after │ │ heap-B │ heap-B vs base │ Tsc/bluesky 934.8Mi ± 0% 934.8Mi ± 0% ~ (p=0.798 n=8) Tsc/sentry 2.583Gi ± 0% 2.603Gi ± 0% +0.78% (p=0.000 n=8) Tsc/t3code-server 2.295Gi ± 0% 2.312Gi ± 0% +0.71% (p=0.000 n=8) geomean 1.756Gi 1.765Gi +0.50% --- tsc/internal/checker/checker.go | 30 ++++++++++++------------- tsc/internal/checker/emitsupport.go | 2 +- tsc/internal/checker/inference.go | 4 ++-- tsc/internal/checker/nodebuilderimpl.go | 12 +++++----- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 10ddb3c0c88b4..7484b90f10f28 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -705,22 +705,22 @@ type Checker struct { switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] computedNameLinks core.LinkStore[*ast.Node, ComputedNameNodeLinks] - symbolReferenceLinks core.LinkStore[*ast.Symbol, SymbolReferenceLinks] + symbolReferenceLinks symbolLinkStore[SymbolReferenceLinks] valueSymbolLinks symbolLinkStore[ValueSymbolLinks] - mappedSymbolLinks core.LinkStore[*ast.Symbol, MappedSymbolLinks] - deferredSymbolLinks core.LinkStore[*ast.Symbol, DeferredSymbolLinks] - aliasSymbolLinks core.LinkStore[*ast.Symbol, AliasSymbolLinks] - moduleSymbolLinks core.LinkStore[*ast.Symbol, ModuleSymbolLinks] - lateBoundLinks core.LinkStore[*ast.Symbol, LateBoundLinks] - exportTypeLinks core.LinkStore[*ast.Symbol, ExportTypeLinks] - membersAndExportsLinks core.LinkStore[*ast.Symbol, MembersAndExportsLinks] - typeAliasLinks core.LinkStore[*ast.Symbol, TypeAliasLinks] - declaredTypeLinks core.LinkStore[*ast.Symbol, DeclaredTypeLinks] - spreadLinks core.LinkStore[*ast.Symbol, SpreadLinks] - varianceLinks core.LinkStore[*ast.Symbol, VarianceLinks] - ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks] - markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks] - symbolContainerLinks core.LinkStore[*ast.Symbol, ContainingSymbolLinks] + mappedSymbolLinks symbolLinkStore[MappedSymbolLinks] + deferredSymbolLinks symbolLinkStore[DeferredSymbolLinks] + aliasSymbolLinks symbolLinkStore[AliasSymbolLinks] + moduleSymbolLinks symbolLinkStore[ModuleSymbolLinks] + lateBoundLinks symbolLinkStore[LateBoundLinks] + exportTypeLinks symbolLinkStore[ExportTypeLinks] + membersAndExportsLinks symbolLinkStore[MembersAndExportsLinks] + typeAliasLinks symbolLinkStore[TypeAliasLinks] + declaredTypeLinks symbolLinkStore[DeclaredTypeLinks] + spreadLinks symbolLinkStore[SpreadLinks] + varianceLinks symbolLinkStore[VarianceLinks] + reverseMappedSymbolLinks symbolLinkStore[ReverseMappedSymbolLinks] + markedAssignmentSymbolLinks symbolLinkStore[MarkedAssignmentSymbolLinks] + symbolContainerLinks symbolLinkStore[ContainingSymbolLinks] externalModuleContainers *externalModuleContainerIndex sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] regExpScanner *scanner.Scanner diff --git a/tsc/internal/checker/emitsupport.go b/tsc/internal/checker/emitsupport.go index 3da782c9ab4c4..7999bd60f0265 100644 --- a/tsc/internal/checker/emitsupport.go +++ b/tsc/internal/checker/emitsupport.go @@ -293,7 +293,7 @@ func (c *Checker) requiresAddingImplicitUndefined(declaration *ast.Node, symbol } t := c.getTypeOfSymbol(symbol) c.mappedSymbolLinks.Has(symbol) - return (symbol.Flags()&ast.SymbolFlagsProperty != 0) && (symbol.Flags()&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && c.ReverseMappedSymbolLinks.Has(symbol) && c.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(c, t) + return (symbol.Flags()&ast.SymbolFlagsProperty != 0) && (symbol.Flags()&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && c.reverseMappedSymbolLinks.Has(symbol) && c.reverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(c, t) case ast.KindParameter, ast.KindJSDocParameterTag: return c.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration) default: diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index 02c7d009b9ec5..420697fc1bc07 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -1133,7 +1133,7 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { inferredProp := c.newSymbolEx(ast.SymbolFlagsProperty|prop.Flags()&optionalMask, prop.Name(), checkFlags) inferredProp.SetDeclarations(prop.Declarations()) c.valueSymbolLinks.Get(inferredProp).setNameType(c, c.valueSymbolLinks.Get(prop).getNameType()) - links := c.ReverseMappedSymbolLinks.Get(inferredProp) + links := c.reverseMappedSymbolLinks.Get(inferredProp) links.propertyType = c.getTypeOfSymbol(prop) constraintTarget := r.constraintType.AsIndexType().target if constraintTarget.flags&TypeFlagsIndexedAccess != 0 && constraintTarget.AsIndexedAccessType().objectType.flags&TypeFlagsTypeParameter != 0 && constraintTarget.AsIndexedAccessType().indexType.flags&TypeFlagsTypeParameter != 0 { @@ -1156,7 +1156,7 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { func (c *Checker) getTypeOfReverseMappedSymbol(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) if links.resolvedType == nil { - reverseLinks := c.ReverseMappedSymbolLinks.Get(symbol) + reverseLinks := c.reverseMappedSymbolLinks.Get(symbol) links.resolvedType = core.OrElse(c.inferReverseMappedType(reverseLinks.propertyType, reverseLinks.mappedType, reverseLinks.constraintType), c.unknownType) } return links.resolvedType diff --git a/tsc/internal/checker/nodebuilderimpl.go b/tsc/internal/checker/nodebuilderimpl.go index 3d84ff3bd4094..7b6c131a3e554 100644 --- a/tsc/internal/checker/nodebuilderimpl.go +++ b/tsc/internal/checker/nodebuilderimpl.go @@ -2408,8 +2408,8 @@ func (b *NodeBuilderImpl) shouldUsePlaceholderForProperty(propertySymbol *ast.Sy // (2) if len(b.ctx.reverseMappedStack) > 0 { last := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1] - if b.ch.ReverseMappedSymbolLinks.Has(last) { - links := b.ch.ReverseMappedSymbolLinks.TryGet(last) + if b.ch.reverseMappedSymbolLinks.Has(last) { + links := b.ch.reverseMappedSymbolLinks.TryGet(last) propertyType := links.propertyType if propertyType != nil && propertyType.objectFlags&ObjectFlagsAnonymous == 0 { return true @@ -2422,10 +2422,10 @@ func (b *NodeBuilderImpl) shouldUsePlaceholderForProperty(propertySymbol *ast.Sy if len(b.ctx.reverseMappedStack) < MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { return false } - if !b.ch.ReverseMappedSymbolLinks.Has(propertySymbol) { + if !b.ch.reverseMappedSymbolLinks.Has(propertySymbol) { return false } - propertyLinks := b.ch.ReverseMappedSymbolLinks.TryGet(propertySymbol) + propertyLinks := b.ch.reverseMappedSymbolLinks.TryGet(propertySymbol) propMappedType := propertyLinks.mappedType if propMappedType == nil || propMappedType.symbol == nil { return false @@ -2435,8 +2435,8 @@ func (b *NodeBuilderImpl) shouldUsePlaceholderForProperty(propertySymbol *ast.Sy break } prop := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1-i] - if b.ch.ReverseMappedSymbolLinks.Has(prop) { - links := b.ch.ReverseMappedSymbolLinks.TryGet(prop) + if b.ch.reverseMappedSymbolLinks.Has(prop) { + links := b.ch.reverseMappedSymbolLinks.TryGet(prop) mappedType := links.mappedType if mappedType != nil && mappedType.symbol == propMappedType.symbol { return true