Skip to content

perf: stop the freeze on huge conversations and speed up filtering#20

Merged
brtkwr merged 1 commit into
mainfrom
perf/cache-to-main
Jun 30, 2026
Merged

perf: stop the freeze on huge conversations and speed up filtering#20
brtkwr merged 1 commit into
mainfrom
perf/cache-to-main

Conversation

@brtkwr

@brtkwr brtkwr commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Gets the perf fixes onto main. #19 was stacked on #18's branch and merged into that branch instead of main (same stacked-PR trap as #16/#17), so the perf work never reached main and isn't in the latest release. This replays it onto current main (which already has #18's mouse-leak fix).

Three hot-path optimisations, all measured on the real 314-conversation corpus:

  1. HITS cache (the freeze). formatListItem rescanned every message of every visible row each frame; holding a clamped nav key on a 6k-message conversation queued ~65ms frames into a multi-second freeze. Memoised per query (keyed by SessionID): View 65ms → 4.7ms/frame.
  2. Preview-line cache. renderPreview/maxPreviewScroll no longer rebuild the preview every frame.
  3. Incremental filtering. When the new query contains the previous one, updateFilter filters the previous (smaller) result set instead of rescanning all conversations: narrowing keystroke ~10ms → ~1-2ms.

Caches are pointer-held so they survive the value-receiver copies View makes, and invalidate on query/selection change.

Tests: TestHitCountCachedPerQuery, TestPreviewLinesCachedUntilSelectionOrQueryChanges, TestUpdateFilterIncrementalNarrowing. go vet clean; coverage 63.6%.

Three hot-path fixes, all measured on the real corpus:

1. Memoise HITS per query (hitCounter), keyed by SessionID. formatListItem
   was rescanning every message of every visible row each frame; holding a
   nav key at a list boundary on a 6k-message conversation queued ~65ms
   frames into a multi-second freeze. Cached: View drops 65ms -> 4.7ms/frame.

2. Memoise the selected conversation's preview lines (previewCache), keyed
   by SessionID+query, so renderPreview/maxPreviewScroll don't rebuild
   every frame.

3. Incremental filtering: when the new query contains the previous one,
   every new match already matched the old query, so updateFilter filters
   the previous (smaller) result set instead of rescanning all
   conversations. Steady-state typing of a narrowing query: ~10ms -> ~1-2ms.

Caches are pointer-held so they survive the value-receiver copies View
makes, and invalidate when the query/selection changes.
@brtkwr brtkwr merged commit 15f309a into main Jun 30, 2026
1 check 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.

1 participant