diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-25 18:41:18 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-25 18:41:18 -0300 |
| commit | 1cef9c2e4bd873ebe13f5df635899231bcc467d2 (patch) | |
| tree | ce0a9495bc801666ff14da47f7a75728335cc442 /experiments/ops-ReleaseSmall.csv | |
| parent | ef6f3e460ddbf53a752807bcf10a0fec0a72da7f (diff) | |
| download | esp32p4-1cef9c2e4bd873ebe13f5df635899231bcc467d2.tar.gz esp32p4-1cef9c2e4bd873ebe13f5df635899231bcc467d2.zip | |
A measuring instrument, and what it says about where the latency goes
"Too slow for interactive use" is a real complaint and not a number. This adds the
number, and the number says the wire is innocent.
## The instrument
`tools/perfproto.zig` is a small framed protocol - "P4", op, length, CRC-32 of the
payload, payload - shared VERBATIM by the host tool and `examples/uartperf.zig`, so
a frame one writes and the other parses cannot drift. It is imported as a module by
both, not copied.
The checksum is the whole point. RX overrun on this UART is undetected in hardware
and uncounted in the driver, so a byte that never arrived is indistinguishable from
a late one; a throughput figure that is not checksummed is a guess about how fast
data was corrupted. `sink` accumulates a CRC over every payload byte the board
received and `report` hands it back, so the host can prove that what arrived is
what it sent.
`tools/rtt.zig` is the two timing functions: `roundTrip` and `measure`. Round trip
is to the FIRST response byte, deliberately. A renderer that starts drawing in 8 ms
and finishes in 130 ms feels immediate; one that thinks for 130 ms and then draws in
8 ms feels broken; waiting for the wire to fall quiet cannot tell them apart. Time
to the last byte is recorded separately as `settle`. Microseconds, because at 115200
one byte is 87 us and a millisecond clock quantises the answer into buckets eleven
bytes wide.
`tools/bench_main.zig` is `p4-bench`: `--link` for the ceiling, `--editor` for how
much of it the editor uses, `--sweep` for one controlled variable at a time with
`--csv` raw per-trial output.
## What it measured
The link is essentially perfect: 11,496 B/s up and 11,413 B/s down, 99.8% of
capacity in both directions, CRC verified over 32,768 B each way, zero corruption.
Typing at 6 to 100 keys/s loses nothing and never uses more than 9% of the wire, so
H5 - "typing loses input" - is refuted.
Latency is compute per input event, not transmission. A 40-byte motion and a
206-byte insert-and-escape cost the SAME round trip to within 0.3 ms, across a
five-fold range of output. That is why raising the baud cannot fix typing: there is
almost no wire in it.
And an edit costs the whole document. Round trip against characters already in the
line is a straight line at 54.3 us per character per keystroke - 17.0 ms at an empty
line, 25.6 ms at 160. On a ~90 MHz core that is ~5,000 cycles per character, far
more than a copy alone, so the full-buffer copy the source does is accompanied by at
least one more full pass.
One controlled intervention: building the editor object ReleaseFast instead of
ReleaseSmall cuts the fixed cost 13% and the per-character cost 36%, for 35% more
flash (809,536 B of a 1,536,000 B partition). Its advantage grows with the document.
Nothing else measured comes close to that ratio.
## Three bugs found while building it
The responder printed garbage and looked dead: it read `.rodata` before evicting the
bootloader's stale cache lines. `flushFlashCache` moved from `src/pardes/app.zig` to
`soc.zig` with its measured evidence, since every application that touches `.rodata`
after hand-over needs it and exactly one file knew that.
Then it booted, printed its marker and went silent after ten seconds:
`rst:0x10 (CHIP_LP_WDT_RESET)`. The bootloader arms the RTC watchdog and expects the
application to take it over. Only the editor ever did.
`serial.Port.drain()` drains INPUT, not output - so timing a transfer to it reported
202% of the wire's capacity and ate the reply. Added `flushOutput` (tcdrain), named
so the two cannot be confused again.
Also: Zig 0.16 emits an explicit `+` for a non-negative SIGNED integer whenever a
width is given (std/Io/Writer.zig:1548-1559), which put a `+` in front of every
number in the first tables.
## The report
`experiments/report.typ` reads the raw CSVs and computes its own figures, so a
re-run changes the document instead of contradicting it. It states five hypotheses,
settles each against one experiment, and is explicit about the one that failed: the
geometry sweep is confounded, because characters accumulated across conditions and
the length experiment then proved that matters. It is reported as unsupported rather
than dressed up as a result.
Diffstat (limited to 'experiments/ops-ReleaseSmall.csv')
| -rw-r--r-- | experiments/ops-ReleaseSmall.csv | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/experiments/ops-ReleaseSmall.csv b/experiments/ops-ReleaseSmall.csv new file mode 100644 index 0000000..daa51f4 --- /dev/null +++ b/experiments/ops-ReleaseSmall.csv @@ -0,0 +1,48 @@ +label,experiment,cols,rows,length,op,rep,rtt_us,settle_us,bytes +ReleaseSmall,ops,0,0,0,motion_h,0,16719,19345,40 +ReleaseSmall,ops,0,0,0,motion_h,1,16770,19401,40 +ReleaseSmall,ops,0,0,0,motion_h,2,16666,19240,40 +ReleaseSmall,ops,0,0,0,motion_h,3,16741,19292,40 +ReleaseSmall,ops,0,0,0,motion_h,4,16765,19437,40 +ReleaseSmall,ops,0,0,0,motion_h,5,16699,19235,40 +ReleaseSmall,ops,0,0,0,motion_h,6,16685,19257,40 +ReleaseSmall,ops,0,0,0,motion_l,0,16688,19418,40 +ReleaseSmall,ops,0,0,0,motion_l,1,16826,19445,40 +ReleaseSmall,ops,0,0,0,motion_l,2,16781,19317,40 +ReleaseSmall,ops,0,0,0,motion_l,3,16694,19326,40 +ReleaseSmall,ops,0,0,0,motion_l,4,16677,19256,40 +ReleaseSmall,ops,0,0,0,motion_l,5,16645,19276,40 +ReleaseSmall,ops,0,0,0,motion_l,6,16709,19276,40 +ReleaseSmall,ops,0,0,0,line_start,0,16795,19362,40 +ReleaseSmall,ops,0,0,0,line_start,1,16698,19319,40 +ReleaseSmall,ops,0,0,0,line_start,2,16820,19407,40 +ReleaseSmall,ops,0,0,0,line_start,3,16716,19287,40 +ReleaseSmall,ops,0,0,0,line_start,4,16760,19405,40 +ReleaseSmall,ops,0,0,0,line_start,5,16725,19450,40 +ReleaseSmall,ops,0,0,0,line_start,6,16769,19315,40 +ReleaseSmall,ops,0,0,0,line_end,0,16681,19296,40 +ReleaseSmall,ops,0,0,0,line_end,1,16628,19240,40 +ReleaseSmall,ops,0,0,0,line_end,2,16796,19343,40 +ReleaseSmall,ops,0,0,0,line_end,3,16678,19242,40 +ReleaseSmall,ops,0,0,0,line_end,4,16703,19396,40 +ReleaseSmall,ops,0,0,0,line_end,5,16693,19438,40 +ReleaseSmall,ops,0,0,0,line_end,6,16699,19341,40 +ReleaseSmall,ops,0,0,0,insert_esc,0,16784,46055,265 +ReleaseSmall,ops,0,0,0,insert_esc,1,16819,40714,204 +ReleaseSmall,ops,0,0,0,insert_esc,2,17244,37382,206 +ReleaseSmall,ops,0,0,0,insert_esc,3,16897,40737,206 +ReleaseSmall,ops,0,0,0,insert_esc,4,17236,37425,206 +ReleaseSmall,ops,0,0,0,insert_esc,5,17006,40907,206 +ReleaseSmall,ops,0,0,0,insert_esc,6,17159,41133,206 +ReleaseSmall,ops,0,0,0,repaint_39,0,17034,36306,81 +ReleaseSmall,ops,0,0,0,repaint_39,1,16983,36314,80 +ReleaseSmall,ops,0,0,0,repaint_39,2,17159,36171,80 +ReleaseSmall,ops,0,0,0,repaint_39,3,16913,36217,80 +ReleaseSmall,ops,0,0,0,repaint_39,4,10054,134042,1253 +ReleaseSmall,ops,0,0,0,repaint_39,5,16638,35615,80 +ReleaseSmall,ops,0,0,0,repaint_40,0,10018,135552,1265 +ReleaseSmall,ops,0,0,0,repaint_40,1,17099,36357,80 +ReleaseSmall,ops,0,0,0,repaint_40,2,16988,36299,80 +ReleaseSmall,ops,0,0,0,repaint_40,4,17031,36233,80 +ReleaseSmall,ops,0,0,0,repaint_40,5,17013,36330,80 +ReleaseSmall,ops,0,0,0,repaint_40,6,16957,36281,80 |
