Skip to content

perf(sysstat): read the focused pane's process tree from /proc, not ps - #306

Merged
tfyl merged 2 commits into
mainfrom
perf/trees-from-proc
Oct 11, 2026
Merged

tfyl merged 2 commits into
mainfrom
perf/trees-from-proc

Conversation

@tfyl

@tfyl tfyl commented Oct 9, 2026

Copy link
Copy Markdown
Member

Why

While a pane is focused, the board refreshes its process line (CPU, RAM, what the pane runs) every 1.2 s, and each refresh forks ps twice. The first fork lists every process on the machine to report on the few under one pane. On a 1,636-process host that costs 159 ms per refresh, about 13% of a core spent on ps alone. A 60 s execsnoop of a live board counted 80 ps forks a minute.

Summary

  • The new sysstat.Tree(pid) walks the pane's tree from its root through /proc/<pid>/task/*/children, using the walker the poll pass's TreeSampler already relies on. It forks nothing.
  • PCPU is computed the way ps computes %cpu: each process's CPU time over its lifetime, using starttime from /proc/<pid>/stat and /proc/uptime. ScaleToHost then gets the same input it got from ps.
  • Without /proc child lists (macOS, or a kernel without CONFIG_PROC_CHILDREN), Tree falls back to Trees unchanged.
  • The two focus-capture call sites use Tree. Trees, and the TreeSampler seeding scan that tests check against ps, stay as they are.
  • The other ps sites are left alone because no timer drives them. The child sweep's background-work check runs only for a finished child about to be retired, and SessionOfProcess runs once per caller, not on a clock.

Numbers

BenchmarkFocusedPaneTree, 50 iterations on a host with 1,636 processes:

per refresh ps forks
Trees (before) 158.9 ms 2
Tree (after) 0.053 ms 0

Test plan

  • TestTreeAgreesWithTreesWithoutForkingPs: on a fixture tree, Tree matches Trees exactly on process count and children, keeps RSS within TreeSampler's 2× bound, and keeps PCPU within 0.5. A ps shim on PATH proves it never forks; passes 5/5
  • go test ./internal/sysstat/ and go test -short ./internal/ui/ pass, run with env -u TMUX -u TMUX_PANE and a scratch TMUX_TMPDIR
  • Nothing the board draws changes (same fields, same scaling), so this PR has no capture

🤖 Generated with Claude Code

The focused pane's process line called sysstat.Trees every 1.2 s, which
forks ps twice and walks every process on the machine to report on the few
under one pane. Tree walks /proc from the root on Linux, with ps's
lifetime %cpu computed from each process's start time, and falls back to
Trees elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tfyl tfyl added the claude PR primarily authored by Claude label Oct 9, 2026
Tree read each process's resident pages from /proc/<pid>/stat. The kernel
sums that count from per-CPU counters without folding them in, so on the
small processes under a pane it ran about a tenth below what ps reported
through Trees: the focused pane's line showed 11.6MB where ps saw 12.5MB.
Tree now reads statm, which agrees with ps to the page, and the test that
compares the two holds RSS to 2% instead of 2x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tfyl
tfyl merged commit cc0ae63 into main Oct 11, 2026
7 checks passed
@tfyl
tfyl deleted the perf/trees-from-proc branch October 11, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude PR primarily authored by Claude

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant