docs: a pause publish costs no packets, and the look is checked with frame path
An A/B of the same fast-mock run with and without a pause reads gaps=0 both ways, the paused one having published in 86 ms -- so the gaps=73 recorded yesterday as "a pause costs packet gaps, unexplained" was incidental load on one run. It never scaled with the publish length and has not reproduced. Record the publish figures as the range they actually span across runs (event 69-115 ms, pause 22-86 ms) rather than as single readings, and name `frame path:` across a pause as the check that the paused trace is drawn like a live one -- raw=1 lod=555 against raw=0 lod=373, where the defect read raw=21 lod=118. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d2c51f2448
commit
f307d27e41
3 files changed
+37
-23
No files matched your search
@@ -726,20 +726,22 @@ Two lines of the self-check output are assertions in disguise:
|
|||||||
`publish_event()` runs on the producer with `mutex()` already held, so its cost lands on the UI
|
`publish_event()` runs on the producer with `mutex()` already held, so its cost lands on the UI
|
||||||
as a long tick; `publish_pause_view()` runs on the UI thread and takes the lock, so its cost
|
as a long tick; `publish_pause_view()` runs on the UI thread and takes the lock, so its cost
|
||||||
lands on the producer and can only surface as `gaps`. Read the event figure against `tick`'s max
|
lands on the producer and can only surface as `gaps`. Read the event figure against `tick`'s max
|
||||||
and the pause figure against `gaps`. Measured on the fast mock over a 3 s run: one event at
|
and the pause figure against `gaps`. Both are **wide**: on the fast mock the event publish has
|
||||||
**115 ms** and one pause at **22.3 ms** — the event figure is far above the ~40 ms the `tick`
|
read **69, 89, 92 and 115 ms** and the pause publish **22 and 86 ms** across runs, so treat the
|
||||||
entry above quotes, so treat that 40 as the old reading and this line as the authority.
|
line as an order of magnitude and not a figure to compare a change against at the millisecond.
|
||||||
|
The event reading matters anyway because it is far above the ~40 ms the `frame cost:` entry above
|
||||||
|
quotes — treat that 40 as the old reading and this line as the authority.
|
||||||
It is also **the number to judge a second live ring against**, which is the one design question
|
It is also **the number to judge a second live ring against**, which is the one design question
|
||||||
it exists to answer: freezing a whole `BufferStore` copies nothing and inherits the pyramid a
|
it exists to answer: freezing a whole `BufferStore` copies nothing and inherits the pyramid a
|
||||||
`ChannelBuffer` already maintains incrementally, so both `worst` figures would go to **0** — at
|
`ChannelBuffer` already maintains incrementally, so both `worst` figures would go to **0** — at
|
||||||
the price of a second budget's worth of memory (README records that one live ring is why
|
the price of a second budget's worth of memory (README records that one live ring is why
|
||||||
`--buffer-mb 200` really is 200 MB) and a second per-sample write on the producer. Two things
|
`--buffer-mb 200` really is 200 MB) and a second per-sample write on the producer. What the line
|
||||||
make the trade less obvious than the memory arithmetic suggests, and both were measured rather
|
says about that trade is that the stall is real but **costs no packets**: an A/B of the same run
|
||||||
than reasoned: the event publish really is ~115 ms of blocked UI, and **a pause costs `gaps`
|
with and without a pause read `gaps=0` both ways, the paused one with an 86 ms publish, so a
|
||||||
where a run without one reads `gaps=0`** (73 with the pause against 0 without). That second
|
second ring would buy a smoother frame at a latch rather than recovered data. (An earlier run
|
||||||
finding is *not* the publish length — it read 79 gaps with the pyramid build removed and 73 with
|
reported `gaps=73` with a pause against 0 without, which is what made the stall worth measuring;
|
||||||
it, i.e. it does not scale with the stall — so it is **unexplained**, and anyone pricing a second
|
it has not reproduced and did not scale with the publish length — 79 gaps with the pyramid build
|
||||||
ring should find out what it actually is first rather than assume the ring would fix it.
|
removed against 73 with it — so it was incidental load on one run. Do not quote it as a cost.)
|
||||||
- `trace ink: N paints checked, C collapsed, S staircase, H short, worst cover P%, worst
|
- `trace ink: N paints checked, C collapsed, S staircase, H short, worst cover P%, worst
|
||||||
drawn-vs-quoted D px over R raw paints` plus a
|
drawn-vs-quoted D px over R raw paints` plus a
|
||||||
`[ink] paint N <flags> mode=.. axis=[..] n=.. idx=[..) spp=.. level=.. pts=.. kept=..
|
`[ink] paint N <flags> mode=.. axis=[..] n=.. idx=[..) spp=.. level=.. pts=.. kept=..
|
||||||
@@ -1173,7 +1175,13 @@ far the largest file.
|
|||||||
anything `publish_event()` does to a snapshot, `publish_pause_view()` owes too. Building it is
|
anything `publish_event()` does to a snapshot, `publish_pause_view()` owes too. Building it is
|
||||||
not free (see `publish cost:` below): it took the pause publish from 6.15 ms to 22.31 ms, a
|
not free (see `publish cost:` below): it took the pause publish from 6.15 ms to 22.31 ms, a
|
||||||
one-off producer stall against ~5 ms a frame of draw for the pause's whole length, which at
|
one-off producer stall against ~5 ms a frame of draw for the pause's whole length, which at
|
||||||
~60 fps repays about thirty times over on a 1.6 s pause.
|
~60 fps repays about thirty times over on a 1.6 s pause — and the producer absorbs it without
|
||||||
|
losing packets (`gaps=0` with and without a pause, the paused run at an 86 ms publish).
|
||||||
|
**The check that the look is restored is `frame path:` across the pause, not the cost line**:
|
||||||
|
a paused run must read the same path as a live one — measured `raw=1 lod=555 columns=555
|
||||||
|
graded=555` against `raw=0 lod=373 columns=373 graded=373`, same 24711-segment worst frame,
|
||||||
|
segment mean within 5 % — where the defect read `raw=21 lod=118`. A `raw` in the tens over a
|
||||||
|
pause is this returning, and it is the appearance as much as the cost.
|
||||||
- **`EventSeries::gather()` emits a bucket's envelope, not every `2^level`-th sample.**
|
- **`EventSeries::gather()` emits a bucket's envelope, not every `2^level`-th sample.**
|
||||||
Point decimation hides exactly the spike the capture was triggered on; a scope has to
|
Point decimation hides exactly the spike the capture was triggered on; a scope has to
|
||||||
draw a bucket's extremes. It needs no index rebasing, unlike the ring: the snapshot's
|
draw a bucket's extremes. It needs no index rebasing, unlike the ring: the snapshot's
|
||||||
|
|||||||
@@ -995,10 +995,17 @@ cheaper case than a capture — it freezes the same rings.**
|
|||||||
It is not free, and the trade is one-off against per-frame. Building the pause pyramid took
|
It is not free, and the trade is one-off against per-frame. Building the pause pyramid took
|
||||||
that publish from 6.15 ms to 22.31 ms, and unlike the event publish this one runs on the
|
that publish from 6.15 ms to 22.31 ms, and unlike the event publish this one runs on the
|
||||||
**UI** thread and so blocks the *producer* — against ~5 ms a frame of draw for as long as the
|
**UI** thread and so blocks the *producer* — against ~5 ms a frame of draw for as long as the
|
||||||
pause lasts, which over a 1.6 s pause at ~60 fps repays thirty times over. One finding there
|
pause lasts, which over a 1.6 s pause at ~60 fps repays thirty times over. The producer
|
||||||
is still open: a run that pauses reports `gaps=73` where the same run without a pause reads
|
absorbs it: an A/B of the same fast-mock run with and without a pause read `gaps=0` both
|
||||||
`gaps=0`, and it does **not** scale with the publish length (79 gaps with the pyramid build
|
ways, with the pause publish at 86 ms in the paused one. (One earlier run reported `gaps=73`
|
||||||
removed, 73 with it), so the stall is not what it is.
|
with a pause against 0 without, which is why the stall was worth measuring at all — but it
|
||||||
|
has not reproduced, it did not scale with the publish length, and an 86 ms publish costing
|
||||||
|
nothing is the stronger reading. Treat that 73 as incidental load on one run.)
|
||||||
|
|
||||||
|
The verification that the *look* is restored is the same A/B read through `frame path:`:
|
||||||
|
`raw=0 lod=373 columns=373 graded=373` without a pause against `raw=1 lod=555 columns=555
|
||||||
|
graded=555` with one — the same path, the same 24711-segment worst frame and a segment mean
|
||||||
|
within 5 % — where before the fix the paused run read `raw=21 lod=118`.
|
||||||
|
|
||||||
The self-check's two `frame cost:` / `frame path:` lines are the guard: they split a frame
|
The self-check's two `frame cost:` / `frame path:` lines are the guard: they split a frame
|
||||||
into the tick, the plot's own draw and the whole GTK paint, and report the segment counts
|
into the tick, the plot's own draw and the whole GTK paint, and report the segment counts
|
||||||
@@ -1765,14 +1772,13 @@ now — they are listed so they are not mistaken for anything else:
|
|||||||
latch after arming with no pre-trigger history. What a second ring would buy is the
|
latch after arming with no pre-trigger history. What a second ring would buy is the
|
||||||
publish: freezing a whole `BufferStore` copies nothing and inherits the pyramid the
|
publish: freezing a whole `BufferStore` copies nothing and inherits the pyramid the
|
||||||
producer already maintains, so both figures in the dump's `publish cost:` line would
|
producer already maintains, so both figures in the dump's `publish cost:` line would
|
||||||
go to zero. They are worth knowing before pricing that — one event publish measured
|
go to zero. They are worth knowing before pricing that — at 1.3 MS/s an event publish
|
||||||
**115 ms of blocked UI tick** at 1.3 MS/s and one pause publish **22 ms of blocked
|
measured **69–115 ms of blocked UI tick** and a pause publish **22–86 ms of blocked
|
||||||
producer** — but they do not pay for a second budget's worth of memory, a second
|
producer** — but they do not pay for a second budget's worth of memory, a second
|
||||||
per-sample write on the producer, and a single frozen slot where the app today holds
|
per-sample write on the producer, and a single frozen slot where the app today holds
|
||||||
an event, a pause and an export's window at once. One thing to settle first: a run
|
an event, a pause and an export's window at once. The *observable* cost is also
|
||||||
that pauses reports `gaps=73` against `gaps=0` without a pause, and that does not
|
smaller than the stall: an A/B with and without a pause read `gaps=0` both ways, so
|
||||||
scale with the publish length, so it is not the stall and a second ring would not
|
what a second ring would buy is a smoother frame at a latch, not recovered packets.
|
||||||
obviously fix it.
|
|
||||||
- **The buffer is allocated at the first CONFIG, not at initialisation.**
|
- **The buffer is allocated at the first CONFIG, not at initialisation.**
|
||||||
The spec's "N+1 split" needs N, which only a CONFIG gives; `--buffer-mb` is the
|
The spec's "N+1 split" needs N, which only a CONFIG gives; `--buffer-mb` is the
|
||||||
*budget*, and the power-of-two ring rounding means the final
|
*budget*, and the power-of-two ring rounding means the final
|
||||||
|
|||||||
+2
-2
@@ -565,8 +565,8 @@ void ScopeModel::publish_pause_view(double lo_s, double hi_s, double x_offset) {
|
|||||||
// It is not free, and the trade is one-off against per-frame: building it took the pause
|
// It is not free, and the trade is one-off against per-frame: building it took the pause
|
||||||
// publish from 6.15 ms to 22.31 ms (`publish cost:`), which blocks the *producer* -- against
|
// publish from 6.15 ms to 22.31 ms (`publish cost:`), which blocks the *producer* -- against
|
||||||
// ~5 ms a frame of draw for as long as the pause lasts, which at ~60 fps a 1.6 s pause repays
|
// ~5 ms a frame of draw for as long as the pause lasts, which at ~60 fps a 1.6 s pause repays
|
||||||
// thirty times over. The gaps a pause costs are **not** this: they measured 79 without the
|
// thirty times over. The producer absorbs the stall: `gaps=0` with and without a pause, the
|
||||||
// pyramid and 73 with, so they are the pause's own and still unexplained.
|
// paused run having published in 86 ms.
|
||||||
auto& pyr = snap->pyramids[c];
|
auto& pyr = snap->pyramids[c];
|
||||||
pyr.reserve(take);
|
pyr.reserve(take);
|
||||||
for (f32 v : vals) {
|
for (f32 v : vals) {
|
||||||
|
|||||||
Reference in new issue
Block a user