← Qnative (provisional) · c3

guide/07-footprint.md

Footprint

Standing: the app's table and Phase 0's table are measured (Phase 0: ../review-0.md). Phase 3 read Live Activity under a stream as first built (../review-3.md); the readings for its 4-a-second redraw are owed.

Some customers will measure what this app costs them. The aim is to be able to answer in numbers, for the app and for the sensor, with developer mode absent, off, and in use.

The app today

Release build, Apple M2 Max, macOS 15.6, synthetic data, idle with no pointer. Measured with strobe 0.26.0 on 2026-11-10 (README, Footprint; cycles/c2.expressive-range/review-3.md).

Reading
Binary23.2 MB
Memory at rest, window on the first page62 MB
Memory after every Inventory page was shown once116 MB
Memory, tray only21 MB
CPU, idle0.005% of one core
Wake-ups per second0.9

Two things these readings already say about developer mode:

  • Showing a page costs more than carrying its code. The 54 MB between the second and third rows is almost all GPU memory for pages that were drawn, and it levels off after the first visit.
  • Idle really is idle. Code that is not running costs no CPU.

What Phase 0 read (2026-11-11)

The same recipe, four states, two interleaved rounds on an idle Mac. A reading starts only after a minute without input and is taken again if anyone touches the machine.

StateWhat it tells usRead
lean buildthe floor: no developer code in the binary23.2 MB binary; 61.6 and 63.0 MB at rest; 0.004–0.006% of a core
full build, developer mode offwhat carrying the code costs24.3 MB binary (+1.07 MB); 61.0 MB at rest (quiet); 0.004%
full build, on, nothing openedwhat the Dev group itself costs61.5 MB (quiet); 0.005%
full build, a developer page shownwhat looking costs65.3 and 65.0 MB with Components shown; 0.005%
full build, allowed, turned off in the traywhat the toggle buys62.8 and 61.8 MB; 0.004–0.006%

The difference between the first two rows is what the lean build buys: 1.07 MB of binary, and nothing at rest that two readings can resolve (one build's repeated readings differ by up to 2.7 MB). The estimate held, so the plan stands.

What Phase 3 read (2026-11-11)

The setup:

  • Live Activity open, with a synthetic coding-session stream at 10, 100 and 1,000 events a second.
  • The full release build, the window on the Studio Display.
  • The same idle rule, and the screen unlocked. A locked screen draws nothing, so it reads low.

As first built, the page redrew once for every batch that arrived:

StateWhat it tells usRead
Live Activity, 10 a secondthe cost of an open stream page107.5 MB, 5.67% of a core (taken while a build ran), 269 wakes a second
100 a second103.4 MB, 27.9%, 1,061 wakes a second
1,000 a second112.2 MB, 33.9%, 1,047 wakes a second
left, then read for 60 swhether closing the page ends the feed95.1 MB, 0.003%, 0.8 wakes a second, 6 threads (8 while open)

What this says:

  • Closing the page ends the feed. The lifecycle rule holds from outside: no CPU, no wake-ups, two threads fewer. The memory the page used stays with the allocator and GPUI's atlases, as after c2's page walk.
  • The cost is the drawing, not the stream.
    • The main thread accounts for nearly all of the CPU: 26–32% of a core at 100 and 1,000 a second.
    • One redraw of the page costs about 10 ms. A build that gathered arrivals for 50 ms read 9.6% main-thread busy at 10 a second, which is ten redraws a second.
    • The stream core alone, with the window covered so nothing draws, costs 0.6%, 1.7% and 7.05% of a core at the three rates. That covers ingest, presence, the tally and the index, for every record.
  • Memory: 92–112 MB while the page is open, against 62–74 MB at rest. The buffer's bound is 20,000 events or 32 MiB.
  • The fix, not yet read: the page now redraws at most 4 times a second, the monitor TUI's cadence. scripts/footprint/live-activity.sh takes those readings. It needs about ten minutes of an idle, unlocked Mac.

The recipe

  1. Build the release binary. Copy it under a distinct file name.
  2. Launch it with QPOINT_DATA=synthetic. Wait 5 s.
  3. strobe record pid:<pid> --for 60s.
  4. Cross-check with footprint -p <pid> and ps -o rss=,pcpu=,cputime= -p <pid>.
  5. Memory is strobe's memory.app.private. Record the display the window was on; GPU-backed memory scales with it.

QPOINT_SPIKE_WALK_AFTER=<s> shows every Inventory page once, for the "after every page" row.

The recipe is now a script:

  • scripts/footprint/measure.sh <label> <binary> takes one reading. It waits for an idle, unlocked Mac, records with strobe, checks that the process survived the minute, and retakes if anything touched the machine.
  • scripts/footprint/live-activity.sh reads Live Activity at each rate, and again after leaving it.

The sensor's side

The app is not the only one that pays. From qcontrol main (2026-11-10):

  • Every subscriber to the event stream makes the monitor re-scan installations and publish two snapshots to all subscribers (throttled to two in 500 ms).
  • Each subscriber has a 1,024-record buffer in the hub. A slow reader is cut off; it does not slow the sensor.
  • Reading the dashboard's API means the dashboard subsystem is on, and it projects the stream into SQLite as it goes.
  • The TUI's own work happens only with --tui: a snapshot of the monitor's state up to four times a second, an index of every tracked process, and a resource sample once a second.

The rule we would keep for anything new the sensor serves to this app: no subscriber, no work.

What we cannot measure from here, and ask for: the monitor's CPU and memory with no subscriber, with one subscriber idle, and with one subscriber during a busy session. The same recipe works against the monitor's pid.

What we will say

If the readings showThe sentence
the lean build, as expected"The production build contains no developer tooling." Read: scripts/check-lean.sh (graph and strings), Phase 0.
developer mode off costs nothing measurable"Where it is allowed, it does no work until it is switched on." Read: Phase 0, rows 2, 3 and 5.
an open stream page has a bounded cost"A page that reads the stream costs at most N MB and M% of a core while it is open, and nothing once it is closed." Read: the second half, Phase 3 ("left": 0.003%, the feed's threads gone). N and M wait for the 4-a-second readings.

No sentence is used before its reading exists.

Qpoint Brand Style Guide