perf: a measurement pane is priced by its window, and its fold is inline
A pane is recomputed over the whole visible window on a cadence, and at a 15 s window (11.8 M samples, 184 k level-0 level buckets, `--buffer-mb 4000` so the stride stays 1) that is priced per *measured channel* and per *distinct share*. Measured with the new `[levels-probe]` sweep, per recompute: one window with one level 6.58 ms, four shares 10.28 ms, sixteen shares 24.55 ms, and four measurements one share each over four channels **35.69 ms** -- against a tick mean of 1.34 ms with an empty pane and 4.58 ms with that one. That is the reported "very slow with multiple measurements": the pass runs inside `tick()`, so it is a *frame* cost, and not an ingest one -- `gaps` and `packets` stay clean even though each `MeasureWindowReader` holds the model mutex for its whole life, because a socket buffer absorbs a few tens of milliseconds of producer. The largest single term inside a pass was a call. `measure_accumulate_bucket()` folds one bucket into one level, and it is the innermost loop of a walk whose iteration count is the window's buckets *times* the pane's levels; it lived out of line in `measure.cpp`, so every one of those iterations was a cross-translation-unit call. Moving it, and the crossing state it updates, into `measure.hpp` as `inline` takes sixteen shares on one channel from 24.55 to **17.25 ms** a recompute -- with every value in the dump's `measurements:` unchanged. Two levers remain and both are measured: the fetch's three out-of-line pyramid accessors plus a 48-byte copy per bucket (3.3 ms of an 11.8 M-sample window's 11.6 ms pass) needs a combined accessor upstream, and `raw` is the *signal's* share (4.6 % on the mock's noisy channel against 0.1 % on its sine), because a level inside a bucket's own `[lo, hi]` has to be walked sample by sample. Bisected with the pane populated (the earlier bisect had it empty, which cannot see this): the parent of the recent range and HEAD read **8.29** and **7.92 ms** a recompute for the same four-type pane at the same window, and neither of the two commits between them touches `ui_measure.cpp` or `measure.cpp` at all. So the cost is the scale and not a regression, and the fix makes a pass cheaper rather than removing one -- the plot's own per-frame cost was checked the same way (four commits, equal within 3 %, and unchanged by the window, the ring size and the visible sample count). `UDPSCOPE_PROBE_LEVELS_MS` with `UDPSCOPE_PROBE_LEVELS` and `UDPSCOPE_PROBE_LEVELS_CHANNELS` is the sweep; `docs/claude/invariants-measurements.md` records the curve and the inline rule, `verification.md` and `probe-readings.md` the probe and what its numbers must read, and the README the pane's cost at this scale.
This commit is contained in:
1 parent
0a209becb1
commit
fc4700d1c9
9 files changed
+242
-61
No files matched your search
@@ -76,6 +76,22 @@ precedent, not an exception.
|
||||
with, so a synchronous sequence would read every position one action stale. It clicks by
|
||||
emitting `::clicked`, never `gtk_widget_activate()`, which holds a `GtkButton` for its
|
||||
250 ms `ACTIVATE_TIMEOUT` and then emits into a button the rebuild has already destroyed.
|
||||
- `[levels-probe] N measurement(s) over K channel(s), shares='...' -> W window(s), L level
|
||||
walk(s)` (stderr, `UDPSCOPE_PROBE_LEVELS_MS`) — **prices a measurement pane**, which is what a
|
||||
report of "it is slow with many measurements" has to become before it is a number. It adds one
|
||||
`above` per share in `UDPSCOPE_PROBE_LEVELS` (comma separated, default `50`), spread in turn
|
||||
over the first `UDPSCOPE_PROBE_LEVELS_CHANNELS` channels (default 1), and reports the two
|
||||
things a count does not name: how many **windows** (one `MeasureWindowReader`, one fetch, one
|
||||
scan per measured channel) and how many **level walks** (one accumulator per *distinct* share)
|
||||
the pass then had to take. `50,50,50,50` on one channel is four pane rows and **one** walk;
|
||||
`5,10,15,20` is four rows and **four**; the same four shares over four channels is four
|
||||
windows *and* four walks. Read the `measure cost:` line it leaves in the exit dump and the
|
||||
sweep in [invariants-measurements.md](invariants-measurements.md) — and read it against a run
|
||||
with `UDPSCOPE_PROBE_LEVELS_MS` unset, because the pane's cost is a *difference* between two
|
||||
tick means. **Run it at the scale being asked about**: the cost is the window's, so a 2 s
|
||||
window prices a 15 s one at nothing (four measurements over four channels read `35.69 ms` a
|
||||
recompute and a 4.58 ms tick mean at a 15 s window against `6.58 ms`/1.34 ms for one window at
|
||||
the same window).
|
||||
- `[measure-probe] N measurements on 'X'` (stderr, `UDPSCOPE_PROBE_MEASURE_MS`) — adds one
|
||||
measurement of each statistical type (average, top, peak to peak, amplitude) on the first
|
||||
*channel* and forces a recompute. It must name a **channel**, not `signals().front()`: the
|
||||
|
||||
Reference in new issue
Block a user