Skip to content

Conversation

@acooks
Copy link
Owner

@acooks acooks commented Dec 28, 2025

No description provided.

acooks and others added 7 commits December 28, 2025 10:39
The throughput chart x-axis label said "Time (ms)" but the values
displayed are actually in seconds. Fixed to show "Time (s)".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
D3's ordinal color scale assigns colors by domain position - the first
key in domain gets the first color (purple), second gets the second
color, etc. When flow rankings change, colorScale.domain(fkeys) is
updated with the new order, but the legend DOM wasn't being updated
to match.

Changes:
- Add colorDomainSet cache to prevent D3's auto-domain-extension when
  getFlowColor() is called with an unknown key (returns grey instead)
- Change legend rebuild check from unordered to ordered comparison,
  since order determines color assignment
- Add merge/update path to update color boxes on existing legend rows
- Add allRows.order() to reorder DOM elements to match data order

The DOM reorder only occurs when flow rankings actually change, not
on every frame, so performance impact is minimal.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Replace CBuffer of objects with TypedRingBuffer classes that store
numeric data directly in Float64Arrays. This eliminates ~64 object
allocations per update cycle for sample and pgap data.

Changes:
- Add TypedRingBuffer2 class for samples (timestamp, value)
- Add TypedRingBuffer4 class for pgaps (timestamp, min, max, mean)
- Update Series constructor to use typed ring buffers
- Update updateSeries() to pass values directly (no object creation)
- Update updateData() to pass values directly (no object creation)
- Update resizeCBuf() and clearSeries() for new buffer types

The typed buffers provide a CBuffer-compatible API (.size, .get(),
.last(), .slice()) so consumer code (updateMainChartData, updateStats,
updatePacketGapChartData) works unchanged.

Object allocation now only happens when reading data for chart updates,
not on every data ingestion. This significantly reduces GC pressure
during high-frequency streaming updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Profile-driven optimization reducing function self-time from 7.6% to 2.2%
(71% reduction).

Key optimizations:
- Use indexed array iteration instead of Set iterator (avoids iterator
  allocation overhead)
- Calculate maxSlice incrementally during bin building (eliminates
  separate O(bins × flows) loop)
- Build formattedData array during bin creation (avoids Map.values()
  iterator)
- Only sort when data is out of order (server typically sends sorted
  data)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Cache D3 selections for axes, grids, and line paths in all chart
modules to avoid repeated svg.select() calls in redraw(). Each
select() creates new selection objects that form cycles with DOM
nodes, triggering expensive Firefox Cycle Collector scans (observed
175ms CC events with 53,920 suspected objects).

The pattern applied:
- Add cachedSelections object with null properties
- Clear selections in reset() before removing DOM (breaks cycles)
- Cache selections when appending elements
- Use cached selections in redraw() instead of svg.select()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add primitive field accessors (timestampAt, valueAt, minAt, maxAt,
meanAt, lastTimestamp, lastValue, lastMin, lastMax, lastMean) that
return values directly without allocating wrapper objects.

Update callers (updateStats, updatePacketGapChartData, updateMainChartData)
to use the new accessors instead of get()/last() which allocate objects.

Also add _selfTest() methods and runRingBufferSelfTests() for verifying
the new accessors match the allocating ones (callable via console).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Instead of clearing arrays and creating new objects each update,
reuse existing objects by mutating their properties. New objects
are only allocated during initial fill.

This reduces Firefox Cycle Collector workload:
- Suspected objects: 53,920 → 18,554 (65% reduction)
- Max CC slice: 20ms → 9ms (55% reduction)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@acooks acooks merged commit 1b089c7 into master Dec 28, 2025
3 checks passed
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