Skip to content

perf(dashboard): single-resolve render data cache for populated dashboard load (260610-ov3)#202

Merged
HanSur94 merged 8 commits into
mainfrom
claude/unruffled-gagarin-e2ca3d
Jun 10, 2026
Merged

perf(dashboard): single-resolve render data cache for populated dashboard load (260610-ov3)#202
HanSur94 merged 8 commits into
mainfrom
claude/unruffled-gagarin-e2ca3d

Conversation

@HanSur94

Copy link
Copy Markdown
Owner

Summary

Optimizes the data-loading slice of populated Tag-bound dashboard loads (quick task 260610-ov3). Complements the live-tick perf passes (260609-v5p, 260610-g0w) — this targets the first render() pass.

  • Render-scoped RenderDataCache_ in FastSenseWidget: one render pass previously resolved the same Tag data 3–4× (bind, Y-autoscale, time-range cache, engine preview pass). Now resolved once and reused. State tags keep the fp.addTag staircase path via a getKind=='state' guard.
  • Consume-once lifetime: cache stays warm through DashboardEngine's post-render computePreviewEnvelope pass, is cleared on the preview read, and refresh()/update() clear it on entry — live ticks never see render-scoped data.
  • O(1) constructor range pull: updateTimeRangeCache() no-arg now uses Tag.getTimeRange() instead of a full getXY(); also fixes disk-backed tags getting inf/-inf time range at construction.
  • Disk-backed widgets: 2 SQLite range queries per render → 1, and they now contribute slider-preview envelopes at load (previously silently empty).
  • New benchmarks/bench_dashboard_load.m (12 tags × 50k pts, 4 disk-backed): Render 6834 → 6711 ms, Create 142 → 123 ms (Octave, graphics-bound — the data-fetch slice is what halved).

No DashboardWidget contract change (additive private state + Hidden test seams only); pure MATLAB, no new dependencies; serialized dashboards unaffected.

Test plan

  • tests/test_fastsense_widget_render_cache.m 4/4 — resolve count ≤1 proven via CountingSensorTag
  • tests/test_dashboard_load_perf.m 5/5 — bound-array parity, preview parity warm vs cold, cache lifecycle, StateTag staircase
  • 13 dashboard/widget regression tests green under Octave 11.1 (incl. serializer round-trip, multipage render, time window, range selector)
  • MISS_HIT mh_lint + mh_style clean
  • MATLAB-only suites (TestDashboardEngine, TestFastSenseWidgetUpdate, TestDashboardSerializerRoundTrip) — deferred, MATLAB MCP down this session (follow-up chip spawned)

🤖 Generated with Claude Code

HanSur94 and others added 8 commits June 10, 2026 17:59
… cache

- test_cache_property_exists: getRenderCacheForTest_ seam cold on construction
- test_resolve_count_le_1: at most 1 getXY call per render() pass
- test_bound_array_parity: inner line XData equals raw tag X (no corruption)
- test_cache_cold_after_render: RenderDataCache_ is cleared after render()
…Widget

260610-ov3: resolve Tag data at most once per render()/rebuildForTag_() pass.

- Add RenderDataCache_ private property (render-scoped struct or [])
- Add pullDataCached_() — returns warm cache or calls pullData_() + caches
- Add cacheRenderData_(x,y) — stores struct into RenderDataCache_
- Add clearRenderCache_() — resets to [] at end of render/rebuild pass
- render(): probe result seeds cache; branch (3) uses pullDataCached_() +
  fp.addLine (State tags keep fp.addTag via getKind guard); yInit and
  updateTimeRangeCache reuse cached x/y; clearRenderCache_() at end
- rebuildForTag_(): same single-resolve + cached updateTimeRangeCache;
  clearRenderCache_() at end
- getRenderCacheForTest_() / setRenderCacheForTest_() Hidden seams for tests
- refresh()/update() untouched — live tick fast path is unaffected
…load perf

260610-ov3 Task 2 RED gate:
- CountingSensorTag.m — SensorTag subclass that counts getXY() invocations
- test_dashboard_load_perf.m — 5 cases:
  - test_resolve_count_le_1: CountingSensorTag asserts <= 1 resolve per render
  - test_bound_array_parity: line XData endpoints within raw tag X range
  - test_preview_parity: warm-cache vs cold-cache getPreviewSeries byte-identical
  - test_cache_cold_after_render: RenderDataCache_ is [] after render completes
  - test_state_tag_fallback: StateTag staircase path still works via getKind guard
260610-ov3 Task 2:

Part A — getPreviewSeries (FastSenseWidget.m):
  Reuses RenderDataCache_ read-only when warm (load-time preview pass).
  Falls back to Tag.getXY() when cold (live ticks) — behavior unchanged.
  Eliminates one extra getXY resolve per widget at dashboard render time.

Part B — benchmarks/bench_dashboard_load.m:
  New benchmark isolating dashboard LOAD (Create+Render) for Tag-bound widgets.
  Builds N_TAGS=12 SensorTags (50k pts each), converts ~1/3 to disk-backed
  via toDisk(), wires N_EVENTS=200 in-memory EventStore to first widget.
  Prints Create / Render / Total ms matching bench_dashboard.m label style.
  Complements bench_dashboard.m (inline XData) and bench_dashboard_live.m
  (live ticks); isolates the disk-backed/Tag-bound render hot path.
…pull O(1)

The executor's render cache was cleared at the end of widget render(),
before DashboardEngine's post-render preview pass — so the preview reuse
was dead code and the constructor still did a full Tag.getXY() pull:

- updateTimeRangeCache() no-arg now asks Tag.getTimeRange() (O(1) for
  Sensor/State tags; disk-backed reads the DataStore extent, which also
  fixes the ctor leaving CachedXMin/Max at inf/-inf for disk tags)
- render() leaves the cache warm; getPreviewSeries consumes it
  (consume-once); refresh()/update() clear it on entry so live ticks
  never read render-scoped data
- setRenderCacheForTest_([], []) clears (test seam)
- tests updated to the lifecycle contract; StateTag test used the
  invalid 'States' option — now 'Labels'

Verified in Octave 11.1: test_fastsense_widget_render_cache 4/4,
test_dashboard_load_perf 5/5, 13 dashboard/widget regression tests OK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Sense dashboards (DashboardEngine)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@HanSur94 HanSur94 merged commit 2355661 into main Jun 10, 2026
10 checks passed
@HanSur94 HanSur94 deleted the claude/unruffled-gagarin-e2ca3d branch June 10, 2026 17:00
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.97436% with 32 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
libs/Dashboard/FastSenseWidget.m 58.97% 32 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

1 participant