Skip to content

Perf: Ring buffer + mutable frameRef for event pipeline #2

@DFearing

Description

@DFearing

Goal

Stop allocating an array copy and a fresh state object on every event commit. Cuts GC churn during normal use and dominates wins during stress bursts (500-event scenario).

Today

  • web/hooks/use-agent-simulation.ts:257-267 — every event commit does eventLog.concat(newEvents) then slice(-MAX_EVENT_LOG) and spreads into a fresh currentState. Two array allocations + a state spread per commit.
  • scripts/relay.ts:94-97 — relay-side has the same shape: buf.slice(buf.length - MAX_EVENT_BUFFER) per event.
  • processEvent returns a new SimulationState per event with shallow-copied internal Maps; a 500-event burst processed in one frame ⇒ ~500 Map allocations.
  • scripts/relay.ts:514-519 — SSE replay sends the entire 5 000-event buffer in a single JSON.stringify on every new client connect.

Plan

  1. Add web/lib/ring-buffer.tsRingBuffer<T>(capacity) with O(1) push, indexed access, chunk-aware iterator.
  2. Replace SimulationState.eventLog: SimulationEvent[] with the ring buffer. Update consumers (timeline rebuild, seek, restart, snapshot/restore).
  3. Allow processEvent to mutate frameRef.current in place during ingestion. Snapshot to React state only at the 4 Hz commit boundary (already throttled). Add an explicit commitSnapshot() that produces an immutable structural copy on demand.
  4. Time-slice ingestion: cap per-frame processing to ~5 ms; defer remaining queued events to the next rAF.
  5. Mirror the relay-side buffer (scripts/relay.ts:80).
  6. Sub-task: stream SSE replay in chunks of ~100 events with setImmediate between chunks.

Acceptance

  • pnpm sim stress shows no frame drops in ?perf overlay; P95 frame time < 20 ms.
  • DevTools heap-snapshot diff: no new state objects allocated per event in steady state.
  • All 24 existing tests pass.

Parallelism

Independent of #6 (renderer) and #3 (stats store). Conflicts with #5 and #4 because all three edit use-agent-simulation.ts. Land this first in Wave A.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions