Skip to content

Improve render perf#21221

Draft
NullVoxPopuli-ai-agent wants to merge 7 commits intoemberjs:mainfrom
NullVoxPopuli-ai-agent:nvp/perf
Draft

Improve render perf#21221
NullVoxPopuli-ai-agent wants to merge 7 commits intoemberjs:mainfrom
NullVoxPopuli-ai-agent:nvp/perf

Conversation

@NullVoxPopuli-ai-agent
Copy link

@NullVoxPopuli-ai-agent NullVoxPopuli-ai-agent commented Mar 16, 2026

note from nvp:

  • initally this is a great example of ai overfitting to a specific problem

@NullVoxPopuli
Copy link
Contributor

NullVoxPopuli commented Mar 16, 2026

image

artifact-1.pdf

    Benchmark Results Summary    

duration phase estimated improvement -2913ms [-2947ms to -2874ms] OR -75.07% [-75.95% to -74.06%]
renderEnd phase no difference [-4ms to 2ms]
render1000Items1End phase estimated improvement -122ms [-133ms to -111ms] OR -81.96% [-89.75% to -74.52%]
clearItems1End phase no difference [-1ms to 11ms]
render1000Items2End phase estimated improvement -67ms [-83ms to -65ms] OR -57.05% [-71.21% to -56.15%]
clearItems2End phase estimated regression +17ms [17ms to 18ms] OR +12.77% [12.44% to 13.31%]
render5000Items1End phase estimated improvement -915ms [-927ms to -893ms] OR -89.54% [-90.79% to -87.46%]
clearManyItems1End phase estimated improvement -170ms [-181ms to -164ms] OR -62.76% [-66.75% to -60.65%]
render5000Items2End phase estimated improvement -828ms [-844ms to -811ms] OR -88.68% [-90.42% to -86.86%]
clearManyItems2End phase estimated improvement -149ms [-149ms to -149ms] OR -99.5% [-99.71% to -99.28%]
render1000Items3End phase estimated improvement -49ms [-50ms to -49ms] OR -49.48% [-49.62% to -49.34%]
append1000Items1End phase estimated improvement -84ms [-85ms to -68ms] OR -62.73% [-63.78% to -51.02%]
append1000Items2End phase estimated improvement -83ms [-100ms to -83ms] OR -62.56% [-74.77% to -62.16%]
updateEvery10thItem1End phase estimated improvement -83ms [-83ms to -66ms] OR -62.01% [-62.18% to -49.8%]
updateEvery10thItem2End phase estimated improvement -83ms [-83ms to -83ms] OR -62.14% [-62.27% to -62.01%]
selectFirstRow1End phase estimated improvement -33ms [-33ms to -33ms] OR -98.82% [-99.08% to -98.55%]
selectSecondRow1End phase estimated improvement -50ms [-50ms to -50ms] OR -99.67% [-99.83% to -99.39%]
removeFirstRow1End phase estimated improvement -83ms [-83ms to -83ms] OR -99.84% [-99.97% to -99.74%]
removeSecondRow1End phase estimated improvement -83ms [-83ms to -83ms] OR -99.88% [-99.99% to -99.84%]
swapRows1End phase estimated improvement -16ms [-16ms to -16ms] OR -24.23% [-24.47% to -24.06%]
swapRows2End phase estimated improvement -16ms [-16ms to -16ms] OR -24.19% [-24.49% to -23.73%]
clearItems4End phase no difference [-24ms to 0ms]


if (Array.isArray(iterable)) {
return new ArrayIterator(iterable, keyFor);
// When iterating a TrackedArray (Proxy), each index access goes through
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is dubious lol

#collection = createUpdatableTag();

#storages = new Map<number, ReturnType<typeof createUpdatableTag>>();
// Use a flat array instead of Map for index-based storage lookups.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also sus

NullVoxPopuli and others added 5 commits March 17, 2026 15:38
Correctness fixes:
- validators.ts: Restore try/finally for isUpdating recovery, use > with
  NaN fallback for volatile tag propagation
- reference.ts: Use track() for error-safe tracking frames (try/catch in
  valueForRef kills V8 JIT optimization)
- iterable.ts: Don't reuse iterator result objects (callers store refs)
- tracking.ts: Avoid corrupting combinator tags when pooling trackers

Performance optimizations:
- reference.ts: Fast path for iterator item refs — skip tracking frame
  and closures entirely since compute only consumes one tag. Eliminates
  ~40,000 allocations per 5000-item list render.
- destroyable: Swap-and-pop removal instead of indexOf+splice, reducing
  array shifting from O(n) to O(1) per removal.
- element-builder.ts: Eliminate First/Last wrapper class allocations,
  store raw SimpleNode references with boolean discriminator.
- tracking.ts: Hand off tags array to combine() instead of slice() copy.
  Remove unwrap() from hot endTrackFrame path.
- update.ts: Remove bulk-clear code that was slower than single-pass
  (3 passes over data vs 1).
- stack.ts: Fast path for capture(0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bug fixes:
- validators.ts: Move isUpdating check BEFORE lastChecked early return.
  During cycle detection, lastChecked is already set to $REVISION, so the
  early return was firing before the cycle could be detected.
- iterable.ts: Handle empty arrays in ArrayIterator.next() — return null
  instead of producing a spurious item with undefined value.
- reference.ts: Use track() for error-safe tracking (try/catch in
  valueForRef or separate function both kill V8 JIT optimization).

Performance optimizations:
- reference.ts: Fast path for iterator item refs — skip tracking frame
  and closures entirely (~40,000 fewer allocations per 5000-item render).
- destroyable: Swap-and-pop removal instead of indexOf+splice.
- element-builder.ts: Eliminate First/Last wrapper allocations.
- tracking.ts: Hand off tags array instead of slice(); remove unwrap()
  from hot endTrackFrame path.
- update.ts: Remove slower bulk-clear code.
- stack.ts: Fast path for capture(0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants