You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
From the validated heap census of claude-code (PERRY_GC_CENSUS, fork/feat/gc-census). Largest single side table, and the largest addressable memory lever now that the regex leak (#9678) is fixed.
perry
node (same bundle)
shape/map count
85,288 descriptors
39,703 V8 Maps
bytes
32.0 MB
3.58 MB
per entry
~394 B
~94 B
Two independent factors: 2.1× more descriptors and ~4× more bytes each.
Worth establishing before optimising
Why 2.1× more shapes than V8 for identical JS? Transition-tree sharing, or shapes minted per-site that V8 unifies? If cc creates 85k distinct object layouts, either the transition chains aren't being shared or something (per-call-site specialisation? the KEYS_INDEX sidecar from perf(object): O(1) own-key answers for wide-object defineProperty and in (#6748) #6749?) is minting duplicates. Halving the count is worth ~16 MB before touching the layout.
What is in a 394-byte descriptor?ShapeDescriptor is copied by value at 63 call sites (see the shape_descriptor_field_by_id note in the campaign's prior work), so its size is already known to matter for speed. An inline key array, a sidecar pointer, and per-field metadata are the likely bulk — a census of the struct itself would say.
Ceiling
Even reaching 2× V8's per-entry cost with the current count is ~16 MB; matching count and density approaches node's 3.6 MB. Realistic target ~8–12 MB, i.e. a 20–24 MB win — the single biggest remaining item.
Verification
PERRY_GC_CENSUS=<path> reports side_tables.shapes directly; node's comparison comes from --heapsnapshot-signal=SIGUSR2 ("object shape" bucket). Both were measured this way. Any change must hold the shape-dependent fast paths green (the IC/transition tests) — a smaller descriptor that loses a field the fast path reads would trade memory for a deopt cliff.
From the validated heap census of claude-code (
PERRY_GC_CENSUS,fork/feat/gc-census). Largest single side table, and the largest addressable memory lever now that the regex leak (#9678) is fixed.Two independent factors: 2.1× more descriptors and ~4× more bytes each.
Worth establishing before optimising
in(#6748) #6749?) is minting duplicates. Halving the count is worth ~16 MB before touching the layout.ShapeDescriptoris copied by value at 63 call sites (see theshape_descriptor_field_by_idnote in the campaign's prior work), so its size is already known to matter for speed. An inline key array, a sidecar pointer, and per-field metadata are the likely bulk — a census of the struct itself would say.Ceiling
Even reaching 2× V8's per-entry cost with the current count is ~16 MB; matching count and density approaches node's 3.6 MB. Realistic target ~8–12 MB, i.e. a 20–24 MB win — the single biggest remaining item.
Verification
PERRY_GC_CENSUS=<path>reportsside_tables.shapesdirectly; node's comparison comes from--heapsnapshot-signal=SIGUSR2("object shape" bucket). Both were measured this way. Any change must hold the shape-dependent fast paths green (the IC/transition tests) — a smaller descriptor that loses a field the fast path reads would trade memory for a deopt cliff.