You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
perf(diagnostics): move the session recorder and feed-debug sinks off the main process #1346
The session recorder and the feed-debug disk sink do their serialization and file I/O off the Electron main process. Main only postMessages.
Actual
Both run on main (fs/promises in SessionRecorder.ts / feedDebugLog.ts).
The recorder time-slices JSON.stringify into ≤ 8 ms chunks with setImmediate yields, but all of that CPU is still main-loop time. The issue measured ~1 MB/s of stringify, 93 % of it screen frames.
queueFeedDebugAppend stringifies per entry with no slicing.
Why separate
It is an architecture change: a worker entry, a structured protocol, backpressure and lifecycle. The app already has three utilityProcess.fork templates: src/main/performance/MonitorCoordinator.ts (with a BoundedQueue envelope), src/main/workflows/ElectronWorkflowWorkerLauncher.ts and src/main/extensions/serviceHost.ts. #767's items 1/3/7 PR makes feed-debug persistence opt-in, so by default this mostly affects dev-debug sessions and recordings.
Severity P3 (only while diagnostics are enabled, after the #767 gate lands).
Measurement before building (W3, 2026-09-27): moving these sinks to a worker would make main slower, not faster.
The test used one real feed-debug file from this machine: 16,832 records, 134 MB, Node 24.14.1, 3 runs each. The time is main-thread CPU to hand every record off:
Hand-off
Time
JSON.stringify each record (what main does today)
333 ms
v8.serialize each record (the structured-clone wire format postMessage and utilityProcess use)
5,029 ms
structuredClone each record
13,955 ms
What follows from this:
Posting objects to a utilityProcess or worker structured-clones them on main, which costs about 15× the stringify we would be moving.
Posting a pre-stringified string still leaves the stringify on main (the part the issue wanted moved). The only thing left to move is the file write, and fs/promises already does that on libuv's thread pool, off the main JS thread.
So the proposed architecture removes little or nothing from the main loop and adds a process, a protocol and backpressure.
This file is feed-debug. The session recorder's screen frames are strings plus small objects, the same shape, so I expect the same result, but no session recording exists locally to measure it.
Proposal: close this as not worth it, or re-scope it to what does help on main:
time-slice queueFeedDebugAppend's stringify the way the recorder already does (≤ 8 ms chunks with setImmediate yields);
Activity
Juliusolsson05 commentedon Sep 27, 2026
Measurement before building (W3, 2026-09-27): moving these sinks to a worker would make main slower, not faster.
The test used one real feed-debug file from this machine: 16,832 records, 134 MB, Node 24.14.1, 3 runs each. The time is main-thread CPU to hand every record off:
JSON.stringifyeach record (what main does today)v8.serializeeach record (the structured-clone wire formatpostMessageandutilityProcessuse)structuredCloneeach recordWhat follows from this:
utilityProcessor worker structured-clones them on main, which costs about 15× the stringify we would be moving.fs/promisesalready does that on libuv's thread pool, off the main JS thread.This file is feed-debug. The session recorder's screen frames are strings plus small objects, the same shape, so I expect the same result, but no session recording exists locally to measure it.
Proposal: close this as not worth it, or re-scope it to what does help on main:
queueFeedDebugAppend's stringify the way the recorder already does (≤ 8 ms chunks withsetImmediateyields);That re-scope overlaps open #1392 (
feedDebugLog.ts), so it would follow #1392. I am waiting on the manager's call before building anything.🤖 Generated with Claude Code