Skip to content

Commit a2bedd7

Browse files
committed
Use paged indexes for type-node and signature caches
Resolved type nodes and signatures still kept AST pointers as map keys despite already having stable node identities. Those maps add redundant pointer storage, GC scanning, hashing, and growth allocations. Reuse the existing node-link store's pointer-free paged indexes for these two high-volume caches. Ordinary IDs and IDs outside the dense reserved range retain the existing fallback, and caches stay checker-local. Six fresh-process interleaved VS Code comparisons against the parent commit, using normal GC, reduce retained heap 1.20%, scan bytes 2.12%, allocated bytes 2.11%, and allocation events 0.20%. Instructions are inconclusive. This saves another roughly 43 MiB of heap and 66 MiB of scanning. Production diagnostics and semantic counts match exactly. Tests cover cache identity, ordinary/reserved IDs, clone isolation, growth, GC reachability, and independent checkers. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.497Gi ± 0% 3.455Gi ± 0% -1.20% (p=0.002 n=6) │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.061Gi ± 0% 2.997Gi ± 0% -2.12% (p=0.002 n=6) │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.129Gi ± 0% 5.021Gi ± 0% -2.11% (p=0.002 n=6) │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 31.16M ± 0% 31.09M ± 0% -0.20% (p=0.002 n=6) │ paged-before-instructions.txt │ paged-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 279.8G ± 5% 270.5G ± 2% ~ (p=0.589 n=6)
1 parent f2c6548 commit a2bedd7

2 files changed

Lines changed: 34 additions & 2 deletions

File tree

‎tsc/internal/checker/checker.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,9 +695,9 @@ type Checker struct {
695695
mergedExportsChecked collections.Set[*ast.Symbol]
696696
factory ast.NodeFactory
697697
nodeLinks core.LinkStore[*ast.Node, NodeLinks]
698-
signatureLinks core.LinkStore[*ast.Node, SignatureLinks]
698+
signatureLinks nodeLinkStore[SignatureLinks]
699699
symbolNodeLinks nodeLinkStore[SymbolNodeLinks]
700-
typeNodeLinks core.LinkStore[*ast.Node, TypeNodeLinks]
700+
typeNodeLinks nodeLinkStore[TypeNodeLinks]
701701
enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks]
702702
assertionLinks core.LinkStore[*ast.Node, AssertionLinks]
703703
arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks]

‎tsc/internal/checker/types_test.go‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,38 @@ import (
1515
"gotest.tools/v3/assert"
1616
)
1717

18+
func TestCheckerNodeLinkStores(t *testing.T) {
19+
t.Parallel()
20+
f := ast.NewNodeFactory(ast.NodeFactoryHooks{})
21+
first := &Checker{}
22+
second := &Checker{}
23+
nodes := make([]*ast.Node, 4096)
24+
for i := range nodes {
25+
node := f.NewIdentifier(strconv.Itoa(i))
26+
if i%2 == 0 {
27+
ast.GetNodeId(node)
28+
}
29+
nodes[i] = node
30+
assert.Assert(t, !first.typeNodeLinks.Has(node))
31+
assert.Assert(t, !first.signatureLinks.Has(node))
32+
first.typeNodeLinks.Get(node).resolvedType = &Type{id: TypeId(i + 1)}
33+
first.signatureLinks.Get(node).resolvedSignature = &Signature{id: SignatureId(i + 1)}
34+
second.typeNodeLinks.Get(node).resolvedType = &Type{id: TypeId(i + 2)}
35+
second.signatureLinks.Get(node).resolvedSignature = &Signature{id: SignatureId(i + 2)}
36+
}
37+
f.ReleaseArenas()
38+
runtime.GC()
39+
for i, node := range nodes {
40+
assert.Equal(t, first.typeNodeLinks.Get(node).resolvedType.id, TypeId(i+1))
41+
assert.Equal(t, first.signatureLinks.Get(node).resolvedSignature.id, SignatureId(i+1))
42+
assert.Equal(t, second.typeNodeLinks.Get(node).resolvedType.id, TypeId(i+2))
43+
assert.Equal(t, second.signatureLinks.Get(node).resolvedSignature.id, SignatureId(i+2))
44+
}
45+
clone := nodes[0].Clone(f)
46+
assert.Assert(t, !first.typeNodeLinks.Has(clone))
47+
assert.Assert(t, !first.signatureLinks.Has(clone))
48+
}
49+
1850
func TestTypeCasts(t *testing.T) {
1951
t.Parallel()
2052
type bases interface {

0 commit comments

Comments
 (0)