Skip to content

perf: Int32Array slab storage for consumer mappings#71

Merged
7rulnik merged 1 commit into
mainfrom
perf/consumer-mapping-slab
May 10, 2026
Merged

perf: Int32Array slab storage for consumer mappings#71
7rulnik merged 1 commit into
mainfrom
perf/consumer-mapping-slab

Conversation

@7rulnik
Copy link
Copy Markdown
Owner

@7rulnik 7rulnik commented May 10, 2026

Summary

Consumer-side counterpart to the generator's MappingList refactor (#64). BasicSourceMapConsumer / IndexedSourceMapConsumer previously held ~350k JS objects per parse for a typical bundle (one per mapping), which dominated init time (_parseMappings was 76% of init) and drove a ~12% GC tax. This PR moves both __generatedMappings and __originalMappings to flat Int32Array slabs with the same 6-field layout the generator already uses.

Hot-path lookups read the slab directly — originalPositionFor and generatedPositionFor now binary-search through typed-array slots, with the existing warm caches and inline GLB/LUB code paths (PRs #68 + previous) ported to slab access. eachMapping keeps the post-#70 stable-shape object iteration via the back-compat _generatedMappings / _originalMappings getters, which lazily materialize an object array from the slab on first access and cache it.

Results (bench-diff vs main, SOLO)

babel.min.js.map

phase Δ
Init speed, JSON input +12.0%
Init speed, Object input +16.3%
Trace speed (random) — originalPositionFor +98.8%
Trace speed (ascending) — originalPositionFor +31.5%
Generated Positions init — generatedPositionFor +90.4%
Generated Positions speed — generatedPositionFor −0.3%
eachMapping speed (generated order) −3.1%
eachMapping speed (original order) +0.9%
mean +30.8%

vscode.map (45MB, ~1.4M segments)

phase Δ
Init speed, JSON input +17.7%
Init speed, Object input +3.6%
Trace speed (random) +69.8%
Trace speed (ascending) +60.5%
Generated Positions init +56.0%
Generated Positions speed +7.5%
eachMapping speed (generated order) −5.4%
eachMapping speed (original order) +3.8%
mean +26.7%

Design notes

  • Slab layout matches MappingList exactlySLAB_FIELDS = 6 with the same field order and the same -1 sentinel. fromSourceMap copies the generator's _buf into the consumer's _genBuf with a single typed-array set() and no per-row decoding.
  • _buildOriginalMappings buckets gen-slab row indices by source, sorts each bucket by (originalLine, originalColumn) — well-formed maps emit per-source in original-position order so the check-sorted pass almost always wins — then copies rows into _origBuf. A parallel _origToGen Int32Array maps orig-row → gen-row so computeColumnSpans / allGeneratedPositionsFor / generatedPositionFor can look up lastGeneratedColumn from the _lastGenCols Int32Array.
  • computeColumnSpans populates _lastGenCols (Int32Array sized to _genCount); the materialized-object cache is invalidated so subsequent _generatedMappings getter access picks up the new lastGeneratedColumn field.
  • eachMapping goes through the _generatedMappings / _originalMappings getter, which lazily materializes a back-compat object array (with the stable hidden class from perf: stable Mapping hidden class in _parseMappings #70) the first time it's accessed. Repeated eachMapping calls iterate that cached array — matching the post-perf: stable Mapping hidden class in _parseMappings #70 baseline. The slab is what makes opf/gpf/init fast; the object-array cache is what keeps eachMapping fast.
  • Input validation for originalPositionFor / generatedPositionFor is now explicit at the entry of those methods — _findMapping (which previously held the throw) is gone since slab lookups are inline.

Test plan

  • yarn test — 205/205 non-TODO tests pass; 8 pre-existing TODO failures unrelated
  • bench-diff.sh main trace on babel.min.js.map — mean +30.8%
  • bench-diff.sh main trace on vscode.map — mean +26.7%
  • eachMapping was the only worry path and lands within noise (±5%) once the materialized-cache getter handles repeated iterations

@7rulnik 7rulnik merged commit c9e422d into main May 10, 2026
3 checks passed
@7rulnik 7rulnik deleted the perf/consumer-mapping-slab branch May 10, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant