perf(task-detail): cache cloud diff stats by content identity#2804
Conversation
Opening a large cloud task froze the renderer. `extractCloudToolChangedFiles` line-diffs every changed file via `getDiffStats`, and it re-runs whenever the session `events` array changes - which during load/stream of a big log happens on every chunk. So every changed file was re-diffed on every update: O(files x updates), the dominant cost (28% / ~2s) when opening the worst tasks. The diff content objects are stable across these recomputes (they are the session events' own objects), so cache `getDiffStats` by diff-object identity in a WeakMap. Repeated recomputes become O(new diffs) instead of O(all files). Measured on a production-scale dataset (CPU profile over CDP, task-load blocked main-thread time): `getDiffStats` dropped from ~2088ms / 28% of the worst load to ~0 (out of the hot path); a representative large cloud task fell 4101ms -> 1282ms blocked. Results are unchanged - the cache only avoids recompute - and a parameterised test pins the diff-stat values plus same-object reuse. This is the cloud-conversation half of the sidebar/cloud freeze pair; the sidebar half is addressed separately by memoizing the task-list rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
Review (paul + xp) noted the previous "cache" test asserted value-equality, which passes even with the WeakMap removed (getDiffStats is pure) - it tested purity, not memoization. Export cachedDiffStats and assert a cache hit returns the same instance (and a distinct-but-equal diff object recomputes), so the test now fails if the cache regresses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Note 🤖 Automated comment by QA Swarm — not written by a human Clean-room review: qa-team, paul-reviewer, xp-reviewer, security-audit — all on Opus. Verdict: ✅ APPROVE (unanimous)All four approved; this is a clean, correct, well-targeted memoization. Verified (the load-bearing correctness question)The cache key — the diff content object — is sound. All four reviewers independently traced it: Actioned (convergent nit, paul + xp) — fixed in d46ed41The original cache test asserted value-equality, which passes even with the WeakMap removed (since Non-blocking notes (left as-is)
Automated by QA Swarm — not a human review |
Problem
Opening a large cloud task froze the renderer (100% CPU, no loaders — synchronous work).
extractCloudToolChangedFilesline-diffs every changed file viagetDiffStats, and it re-runs whenever the sessioneventsarray changes identity. A big session log loads/streams in chunks, so every changed file was re-diffed on every chunk — O(files × updates).Found by loading a production database (766 workspaces, 5.9 GB of logs, biggest single conversation 449 MB / 203k events) into a dev instance and CPU-profiling task loads over CDP.
getDiffStatswas the single biggest frame — 28% / ~2,088ms on the worst load.Change
The diff content objects are stable across these recomputes (they're the session events' own objects), so cache
getDiffStatsresults in aWeakMapkeyed on the diff object. Repeated recomputes become O(new diffs) instead of O(all files). Output is unchanged — the cache only avoids recompute.Measurement
Same CDP profile, before/after:
getDiffStats: ~2,088ms (28% of the worst load) → ~0 (dropped out of the hot path entirely).A parameterised test pins the diff-stat values (added/removed across new/modified/removal) and that reusing the same diff object returns identical results.
Scope
This is the cloud-conversation half of the freeze. The sidebar half (re-rendering all task rows on navigation) is a separate PR (memoizing
TaskRow). Residual cost on the most extreme 449 MB task is now diffuse (log parsing, file-search indexing) with no single dominant frame — a possible future follow-up is incrementalizingbuildCloudEventSummary, but the dominant freeze is resolved.🤖 Generated with Claude Code