Skip to content

perf: optimize deep nested decode#83

Open
samlaycock wants to merge 2 commits into
mainfrom
fix/issue-70-deep-decode-performance
Open

perf: optimize deep nested decode#83
samlaycock wants to merge 2 commits into
mainfrom
fix/issue-70-deep-decode-performance

Conversation

@samlaycock

@samlaycock samlaycock commented May 21, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #70.

  • Cache formatted parent paths while decoding entries so deeply nested sibling paths reuse ancestor path strings instead of rebuilding the same prefixes repeatedly.
  • Defer parsing structural metadata paths until decode actually needs the segments for empty container reconstruction, sparse-array resizing, or Set/Map conversion.
  • Reuse lazily parsed structural path segments across empty-container reconstruction, sparse-array resizing, and Set/Map conversion.
  • Add a patch changeset for the decode performance improvement.

Benchmark notes

Ran bun run bench on Bun 1.3.13 against a main worktree and this branch.

  • main deeply nested object decode: 20.544 ms/op
  • this branch deeply nested object decode after review fix: 18.578 ms/op
  • measured improvement: about 9.6% faster for the deep decode workload

Flat/repeated workloads stayed in the same range in local benchmark runs:

  • large flat object decode: 0.562 ms/op on main, 0.584 ms/op on this branch
  • repeated fields decode: 0.261 ms/op on main, 0.281 ms/op on this branch

Verification

  • bun fmt
  • bun lint:fix
  • bun test
  • bun typecheck
  • bun run build
  • bun lint
  • bun run smoke:package
  • bun run bench

Cache formatted parent paths while decoding entries so deep sibling paths can reuse ancestor path strings instead of rebuilding the same prefixes repeatedly.

Parse structural metadata paths lazily so non-empty object metadata does not pay path parsing cost unless decode needs to materialize an empty container, resize a sparse array, or convert a Set/Map.

Adds a patch changeset for the decode performance improvement.
@samlaycock samlaycock marked this pull request as ready for review May 21, 2026 07:00
@greptile-apps

greptile-apps Bot commented May 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR optimizes decode performance for deeply nested form data by caching intermediate parent-path strings and deferring parsePath calls for structural metadata until they are actually needed.

  • Parent-path caching: addParentPaths replaces the previous buildParentPathSet post-pass. It builds the parentPaths set incrementally while processing entries and caches formatted child paths in a two-level Map<string, Map<PathSegment, string>>, so sibling entries that share deep ancestors skip repeated appendKey/appendIndex calls.
  • Lazy structural-segment parsing: segments is dropped from StructuralPath; structural paths only call parsePath (via getParsedSegments) when actually needed for empty-container reconstruction, sparse-array resizing, or set/map conversion. The parsedSegmentsCache is shared across all three post-unflatten phases, so a path parsed in the empty-container loop is a cache hit in the sparse-array and set/map loops.

Confidence Score: 5/5

Safe to merge. The changes are a self-contained performance optimization with no correctness risk.

Both the incremental parent-path cache and the lazy structural-segment cache are logically equivalent to the pre-PR behavior. The parsedSegmentsCache is shared across all three post-unflatten phases, so a structural path parsed in the empty-container loop is a cache hit in the sparse-array and set/map loops — no path is parsed more than once. The incremental addParentPaths produces the same parentPaths set as the old buildParentPathSet post-pass, just built as entries are processed rather than after. No behavioral changes were made to error handling, validation, or the public API.

No files require special attention.

Important Files Changed

Filename Overview
src/path.ts Adds parsePathEntry helper that combines parsePath + value into a ParsedPathEntry. Minimal, correct refactor.
src/decode.ts Removes segments from StructuralPath, adds incremental parent-path building with a two-level string cache, and lazily parses structural-path segments via parsedSegmentsCache shared across all three post-unflatten phases. Logic is correct; the shared cache prevents double-parsing in the empty-container + sparse-array or set/map branches.
.changeset/optimize-deep-decode.md Adds a patch-level changeset entry describing the decode performance improvement.

Reviews (2): Last reviewed commit: "perf: cache lazy structural path parsing" | Re-trigger Greptile

Comment thread src/decode.ts
Reuse parsed structural path segments across empty-container reconstruction, sparse-array resizing, and Set/Map conversion.

This preserves the lazy parsing path for structural metadata that is skipped while avoiding duplicate parsePath calls for structural paths that decode still needs to handle.
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.

[P2] Optimize deep nested decode performance

1 participant