perf(tui): instrument stdout drain — rule out terminal parse bottleneck
Adds four fields to FrameEvent.phases and the matching profile
summary:
optimizedPatches post-optimize patch count (what's actually
written to stdout; the .patches field is
pre-optimize)
writeBytes UTF-8 byte count of the write this frame
backpressure true when Node's stdout.write returned false
(Writable buffer full — outer terminal can't
keep up)
prevFrameDrainMs end-to-end drain time of the PREVIOUS frame's
write, captured from stdout.write's 2-arg
callback. Reported on the next frame so the
measurement reflects "time until OS flushed
the bytes to the terminal fd", not "time until
queued in Node".
writeDiffToTerminal() now returns { bytes, backpressure } and
accepts an optional onDrain callback. Only attached on TTY with
diff; piped/non-TTY stdout bypasses flow control so the callback
would fire synchronously anyway.
Initial measurements under hold-wheel_up against 1106-msg session
(30Hz for 6s):
patches total 28,888
optimized total 16,700 (ratio 0.58 — optimizer cuts ~42%)
writeBytes 42 KB / 10s = 4.2 KB/s throughput
drainMs p50 0.14 ms terminal accepts bytes instantly
drainMs p99 0.85 ms
backpressure 0% of frames
This rules out the terminal-parse hypothesis — Cursor's xterm.js
drains our output in sub-millisecond time at only 4 KB/s. The
remaining lag has to be in the render pipeline, not the wire.
Profile output now includes the bytes+drain+backpressure lines to
keep this visible on every subsequent iteration.
This commit is contained in:
@@ -149,12 +149,16 @@ export const logFrameEvent = ENABLED
|
||||
flickers: event.flickers.length ? event.flickers : undefined,
|
||||
phases: event.phases
|
||||
? {
|
||||
backpressure: event.phases.backpressure,
|
||||
commit: round2(event.phases.commit),
|
||||
diff: round2(event.phases.diff),
|
||||
optimize: round2(event.phases.optimize),
|
||||
optimizedPatches: event.phases.optimizedPatches,
|
||||
patches: event.phases.patches,
|
||||
prevFrameDrainMs: round2(event.phases.prevFrameDrainMs),
|
||||
renderer: round2(event.phases.renderer),
|
||||
write: round2(event.phases.write),
|
||||
writeBytes: event.phases.writeBytes,
|
||||
yoga: round2(event.phases.yoga),
|
||||
yogaCacheHits: event.phases.yogaCacheHits,
|
||||
yogaLive: event.phases.yogaLive,
|
||||
|
||||
4
ui-tui/src/types/hermes-ink.d.ts
vendored
4
ui-tui/src/types/hermes-ink.d.ts
vendored
@@ -41,6 +41,10 @@ declare module '@hermes/ink' {
|
||||
readonly optimize: number
|
||||
readonly write: number
|
||||
readonly patches: number
|
||||
readonly optimizedPatches: number
|
||||
readonly writeBytes: number
|
||||
readonly backpressure: boolean
|
||||
readonly prevFrameDrainMs: number
|
||||
readonly yoga: number
|
||||
readonly commit: number
|
||||
readonly yogaVisited: number
|
||||
|
||||
Reference in New Issue
Block a user