Skip to content
This repository was archived by the owner on Aug 21, 2026. It is now read-only.

perf: drop per-element wrapper indirection on offset=0 iterator path - #34

Open
Farenheith wants to merge 1 commit into
masterfrom
perf/iterator-wrapper-indirection
Open

perf: drop per-element wrapper indirection on offset=0 iterator path#34
Farenheith wants to merge 1 commit into
masterfrom
perf/iterator-wrapper-indirection

Conversation

@Farenheith

Copy link
Copy Markdown
Member

What

augmentativeIterateIterable previously wrapped every next() call in a () => current() closure even when no skip offset was set. For the common offset=0 case, return finalNext directly as next(), removing one call frame per element for non-array iterables.

Why it's safe

  • Skip path (offset > 0) is byte-identical in semantics to before: stable outer wrapper + internal closure-variable swap. (Reassigning iterator.next after first use would be unsafe — consumers like V8's for...of can cache the function reference, so only an internal variable may change.)
  • Array path and async path untouched (augmentativeIterate dispatches arrays to a separate function; async already has minimal indirection by design).
  • No API or observable behavior change.

Verification

  • 53/53 unit tests pass (also via pre-commit hook)
  • Interleaved A/B benchmark, original vs patched in one process, 100k elements:
Scenario Original Patched Δ
gen→array addMap 2.03ms 1.81ms −10.8%
filter+map chain (gen) 2.08ms 2.01ms −3.8%
skip(1000)+map (array) 4.37ms 4.44ms +1.6% (noise — array path untouched)
  • Correctness: checksums match original on all scenarios incl. skip paths.

Notes

This is the one remaining safe micro-opt from the Aug 2026 perf analysis pass (destructuring, bind-caching and toArray pre-allocation were measured as noise/harmful and ruled out).

augmentativeIterateIterable previously wrapped every next() call in a
'() => current()' closure even when no skip offset was set. For the
common offset=0 case, return finalNext directly as next(), removing one
call frame per element for non-array iterables.

The skip (offset>0) path keeps its original stable-wrapper semantics:
consumers may cache the function reference, so reassigning iterator.next
after first use is unsafe — only an internal closure variable is swapped.

Measured (interleaved A/B, 100k elements): gen->array addMap -10.8%,
filter+map chain -3.8%. Array path and async path are untouched.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant