Skip to content

Four per-render costs: measurements and proposed fixes #571

Open

Description

@zachthedev

Four per-render costs, each with a proposed fix. Three carry a behavior change, one is a ~20-25 file
diff, so they are worth agreeing before the patches.

Windows 11, bun 1.3.13, node 26.7.0. Corpus 18,837 transcripts, 16.2 GiB.

1. jsonl-blocks.ts:183 reads past its own 48 h cap. Parses every byte of every selected
transcript. A backwards scan stopping at the first pre-48 h entry gives the same answer.

current bounded
total 10,443-16,186 ms 2,165-3,056 ms
bytes 1,867 MiB 219 MiB
peak RSS 6.4-8.2 GiB ~460 MiB

Also fixes data loss: readFileSync on a 1,359 MiB transcript throws ERR_STRING_TOO_LONG under
node, the bare catch at :221 returns [], 41,652 timestamps vanish (#550's class).
Behavior change: with no 5 h gap in 48 h and pre-48 h history in a selected file, the block phase
shifts.

2. Git widgets run ten commands where three suffice. 626.3 → 205.3 ms, 21 fewer processes per cold
render, via status --porcelain=v2 --branch -z + diff HEAD --shortstat + remote get-url. Spawn is
~90% of a git call on Windows. Three behavior changes: branch.oid at 7 chars ignores core.abbrev;
conflicted files are double-counted today (git.ts:433-465); getGitChangeCounts double-counts a line
staged then re-modified.

3. chcp.com spawns every Windows render (ccstatusline.ts:325). 35-104 ms, 13-20% of a render;
removing it 401 → 288 ms. The code page is console-wide and persistent, so gating on a session_id
marker keeps #186 fixed.

4. Widget barrel evaluates React and Ink per render. widget-manifest.ts:5 loads all 88 widget
modules; six import ink/react at module scope for renderEditor. 1.57 MB, 55-140 ms, 6-24 MB per
repaint. Stubbed to empty modules, output is byte-identical. Fix is ~20-25 files.

Rejected: lazy import() on runTUI (no win, +4 MB, barrel pulls React anyway); directory-mtime
pruning (NTFS does not bump on append); batched async stat (getBlockMetrics runs inside a sync
render()); fdir withStats (absent on tinyglobby's version).

Activity

  1. changed the title [-]Per-render cost: measured findings, four needing a decision before a patch[/-] [+]Four per-render costs: measurements and proposed fixes[/+] on Aug 31, 2026
  2. maddada commented on Nov 5, 2026

    @maddada

    One more detail for point 3, seen on Windows 11 with 2.2.32 (the call is unchanged on main in src/utils/windows-code-page.ts):

    execFileSync(chcp, ['65001'], { stdio: 'ignore', windowsHide: true }) has windowsHide and no inherited stdio, so libuv starts chcp with CREATE_NO_WINDOW, which gives it a new hidden console of its own. In a process trace from a terminal app that refreshes the status line often, every render started a conhost.exe for chcp, and the chcp 65001 only changed the code page of that throwaway console, not the console ccstatusline itself runs in. The #186 fix (20691a7) ran chcp with stdio: 'ignore' only; windowsHide came later.

    Possible fixes:

    • Drop windowsHide for this call, so chcp shares the inherited console: no extra conhost, and the code page change lands on that console.
    • Or skip chcp when the process has no console to change.
    • Or run it once, as point 3 suggests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions