0d78becb's rules were tested on scripted counters. These two hold them
against this process's own CPU energy counter, each in a test binary of its
own with one test, as eoc-meter's are: the counter's domain is every thread
of the process, and the oracle's check is the process's context-switch count.
Gated the same way: macOS only, and the counter must be live.
The oracle (`tests/oracle`) shares no call and no number with the counter:
`proc_pid_rusage` declared there and read at the SDK's offset, taken once the
test's thread has left its core by the process's context-switch count rising
across a sleep. A `Watched` counter puts that read on either side of every
read the cascade takes, so a span lies between two windows of the oracle's:
the one inside it and the one around it and both reads. That a reading lies
between them holds for any count that only grows, so it is asserted every
time; what is allowed to miss a tenth of the time is how far apart the two
windows are, which is the two readers' own reads and wake-ups.
`hit_calibration_process_energy`:
- each tier calibrated 20 times through the watched counter: the span the
calibration kept is its last two reads, it lies between the oracle's two
windows around the same batch, the per-hit figure times the batch is that
span, and the empty span it reports is the counter's first advance over no
work. The inner window was 952/1000 of the outer at least, quiet and under
40 busy loops, so the figure is pinned within a twentieth; a fifth is
asked.
- on the bare counter, after a tier's first hit, 10,000 hits take no read of
the meter (counted), every one is an estimate, and the microjoules charged
are the calibrated nanojoules to the one microjoule still carried.
- what the change removes: a span around one hit, two reads about one
lookup, reads more than the hit is charged, every time; in an optimized
build 5.8 to 17 times as much at the median, and three times is asked. In an
unoptimized build the lookup itself costs about what the meter does (22 µJ
a cache hit against 1.4 optimized), so no multiple is asked there.
`neural_span_process_energy`, with a KV stage that burns 8 ms of the thread's
CPU on its way to missing:
- a neural stage that burns 2 ms: exactly two reads, the first after the KV
lookup, and the measured figure between the oracle's window inside the span
and its window around it, which opens after the burn. The inner window was
785/1000 of the outer at least under load (992 quiet); two thirds is asked.
- a neural stage that only sleeps 2 ms, on the bare counter, nothing taking
the thread off its core between the burn and the cascade's first read: the
figure is under a quarter of the oracle's window over the whole resolve (at
most 32/1000 of it). A span opened before the walk reads the burn.
No joule figure and no timing is asserted. A calibration that lands on an
efficiency core reads a fifth of what one on a performance core does (253 nJ
a cache hit against 1,400): the figure follows the core the batch ran on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>