CodeNomad/scripts/profile-outline-projection.mjs
Pascal André 6f6edbbcb9
perf(ui): skip redundant outline and superseded Markdown work (#852)
## Summary

I reduced two independently demonstrated sources of redundant work in
the UI shared by Electron and Tauri:

- I reuse the timeline projection when the outline snapshot, resident
marker identities and translated labels have not changed, preserving
reactive locale updates and structural marker changes.
- I discard superseded or disposed Markdown invocations before parsing
and before publishing results, including same-key language retries and
cache hits.

Related to #851, **not a confirmed fix for its Linux Electron freeze**.
I have not reproduced the reporter's environment, and this PR
intentionally does not close the issue. Native events, transcript
authority and OpenCode service lifecycle are unchanged.

## Evidence and validation

- Real native-dispatcher/SessionView fixture with 10k outline entries:
64 sub-bucket deltas trigger **64 full-history projections before / zero
after**; changed text buckets, locale and final text still update.
- Real Markdown/parser instrumentation: cold burst **33 parses before /
one after**, warm burst **32 before / one after**. Disposal, same-key
retries, cached results, theme/highlight, wrap and full-source copy are
covered.
- 100 browser regressions passed (70 renderer/parser/outline + 6
full-source-copy + 24 history-navigation/reasoning), plus 3 outline unit
tests; UI typecheck passed.
- Independent gatekeeper: **zero actionable findings**, with 60 browser
and 6 unit tests passed independently.
- Windows Tauri `build --no-bundle`, packaged-resource smoke and
isolated launch/capture/close passed. I did not replace the installed
app or restart OpenCode. No Electron desktop build/test was run;
Chromium fixture tests do not establish Linux Electron host behavior.
- The initial concurrent history/reasoning regression run hit a shared
Vite cache `EPERM` on Windows and timed out; all 24 affected checks
passed when rerun sequentially.

The broad synthetic stream profile still shows pauses and does **not**
demonstrate an overall responsiveness win. I recorded the measured work
reductions and remaining limits in
`dev-docs/STREAMING_RESPONSIVENESS.md`, with reproducible profiling
scripts.
2026-10-05 22:32:03 +02:00

34 lines
1.9 KiB
JavaScript

// Run with `node --import tsx`; compare --baseline=/path/to/historical.ts.
import { performance } from "node:perf_hooks"
import { fileURLToPath, pathToFileURL } from "node:url"
const baseline = process.argv.find(arg => arg.startsWith("--baseline="))?.slice("--baseline=".length)
const source = baseline ? pathToFileURL(baseline).href : new URL("../packages/ui/src/components/session-outline-projection.ts", import.meta.url).href
const { createSessionOutlineProjection } = await import(source)
const results = []
for (const count of [1000, 10000, 50000]) {
const entries = Array.from({ length: count }, (_, seq) => ({
id: `message-${seq}`, seq, type: "assistant", tools: seq % 3 === 0 ? 1 : 0,
toolName: seq % 3 === 0 ? "read" : undefined, reasoning: 0,
}))
const marker = { id: "stream", messageId: `message-${count - 1}`, type: "assistant",
label: "assistant", tooltip: "streaming", totalChars: 0 }
const project = createSessionOutlineProjection()
let translations = 0
const t = key => { translations++; return key }
const initial = project(entries, [marker], t)
translations = 0
const times = [], publications = []
for (let token = 0; token < 64; token++) {
const start = performance.now()
publications.push(project(entries, [marker], t))
times.push(performance.now() - start)
}
const sorted = [...times].sort((a, b) => a - b)
const updated = project(entries, [{ ...marker, tooltip: "new streamed prefix", totalChars: 128 }], t)
results.push({ count, medianMs: sorted[Math.floor(sorted.length / 2)], maximumMs: Math.max(...times),
totalMs: times.reduce((sum, value) => sum + value, 0), translations,
unchangedArrayIdentity: publications.every(result => result === initial),
changedTextPublished: updated.find(segment => segment.id === `${marker.messageId}:outline`)?.tooltip === "new streamed prefix" })
}
console.log(JSON.stringify({ source: fileURLToPath(source), results }, null, 2))