docs: the millisecond clock stays 64 bits, owner-refused

608 B of flash, 7.6 % of the image, and it is not available: this board runs
continuously and an uptime that restarts every 49.7 days is not acceptable,
which is what a 32-bit millisecond counter wraps at.

The tick split goes with it. It only ever made sense beside the narrowing, and
on its own it costs 66 B to buy about 0.4 % of a CPU nothing here is competing
for.

Both are recorded rather than deleted, and that is the point of the change: the
numbers are real and re-measurable, so someone will find 608 B again and read
it as an opportunity. What they need with it is the reason it was refused.

Docs only; five tests green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 16:31:54 +02:00
parent 8f43584c5c
commit e56e8b977f

View File

@@ -5,35 +5,36 @@ rule 20).
## Open ## Open
- [ ] decide whether the millisecond clock stays 64 bits. **Measured: 608 B of Nothing.
flash, 7.6 % of the image**, plus 16 B of RAM - `uptime::millis()` and the
three timestamps that hold its value narrowed from `uint64_t` to
`uint32_t`, 8004 B down to 7396.
It is not free, and the price is not only the display. A 32-bit ## Decided against
millisecond counter wraps every **49.7 days**, and this board runs
continuously, so two things follow. The `uptime` command would restart
from zero at each wrap. And the interval tests would have to be rewritten
as subtractions - `now - last >= interval` is correct across a wrap where
`now >= last + interval` is not, and `terminal.hpp`'s monitor tick is
written the second way today. That shape is *why* the counter is 64 bits:
it puts the wrap out of reach so a naive comparison cannot be wrong.
So the question is whether a wrapping uptime display is acceptable, and Both of these are measured, and both are recorded here rather than deleted so
the answer decides 608 B. If it is, the three call sites in that the next person to measure them does not read a number as an opportunity
`statistics.hpp` and `terminal.hpp` move to subtraction in the same and re-propose work the owner has already refused.
change, and that is the whole of the work.
- [ ] the 1 kHz tick's own cost, which is separate and smaller. The compare ### The millisecond clock stays 64 bits
handler increments a 64-bit counter, so it calls libgcc's `__adddi3_s8`,
and a call inside a signal handler decides the prologue - twelve push/pop
pairs to cover what the helper might clobber. Splitting the counter into
two 32-bit halves removes the call and halves the prologue: the handler
goes from ~47 instructions to ~26 and keeps the full 64-bit range, the
carry running once every 49.7 days.
**Measured and not taken, because it costs 66 B of flash to save about Narrowing `uptime::millis()` and the three timestamps that hold its value from
0.4 % of the CPU** - `millis()` then reassembles the halves, and the `uint64_t` to `uint32_t` is **608 B of flash, 7.6 % of the image** (8004 B down
readers pay for it. It is the right change only if the tick's cycles ever to 7396), plus 16 B of RAM.
matter; today nothing here is timing-critical. Falls away entirely if the
counter narrows above, which is the reason to decide that one first. **Refused, owner-stated: this board runs continuously and an uptime that
restarts every 49.7 days is not acceptable.** That is what a 32-bit
millisecond counter wraps at, and the display is not the only cost - the
interval tests would have to become subtractions, since `now - last >=
interval` survives a wrap where `now >= last + interval` does not, and
`terminal.hpp`'s monitor tick is written the second way. The 64-bit counter is
what puts the wrap out of reach, which is the property being bought.
### The 1 kHz tick keeps its 64-bit increment
The compare handler increments 64 bits, so it calls libgcc's `__adddi3_s8`, and
a call inside a signal handler decides the prologue - twelve push/pop pairs for
what the helper might clobber. Splitting the counter into two 32-bit halves
removes the call and keeps the full range, taking the handler from ~47
instructions to ~26 with the carry running once every 49.7 days.
**Refused: it costs 66 B of flash to buy about 0.4 % of the CPU**, and nothing
here is timing-critical. It was only ever worth considering alongside the
narrowing above, which is refused outright.