open-standards/eoc/eoc-rs/crates/eoc-cache
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science f84f0b2fd0 eoc-cascade: a cache hit claims no measurement, and a total is measured only if every microjoule was read
c6378629 listed two defects here as not done. `JouleCost::zero()` was tagged
`Measured`, and eoc-cache and the cascade's memoizer stamp every cache hit with
it, so each hit claimed a measured 0 µJ although no meter was read for it. The
receipt seals that tag. And `attach_cascade_cost` added the meter's delta over
the whole resolve to the stage's own cost, then tagged the sum `Measured` as
soon as the delta was positive. With a live counter (RAPL, NVML, a live
IOReport rail) a 50 J estimate plus a 500 µJ reading came out as 50,000,500 µJ
measured. A stage that meters itself on the cascade's counter reads a span
nested inside the cascade's own span, so its reading was counted twice: 300 µJ
inside a 500 µJ span came out as 800 µJ.

The rule now, as the owner set it: a live vendor counter's figure is
`Measured`, and nothing that was not read from one may be. So a total is
measured only when every microjoule of it was read from a live counter.

- `JouleCost::zero()` is `Estimated`. Its doc says what it is: a figure no
  counter read, which is what a cache hit costs by assumption. Every caller
  becomes honest at once: eoc-cache, the memoizer, and eoc-route-learned's two
  empty fallback responses, which also claimed a measured zero. No caller
  needed a measured zero; a counter that read but did not move has measured
  nothing. `JouleSource`'s and `JouleCost::measured`'s docs now say what
  measured means.
- `attach_cascade_cost` takes the span only when it is live: both reads
  succeeded and the counter advanced. Otherwise it adds nothing, and the
  stage's cost stands, figure and tag, as before.
- A live span and an estimated stage: the estimate is added, and the total is
  an estimate. The estimate may be energy the counter never saw (a remote
  backend), so it is added, not dropped.
- A live span and a measured stage: the stage read a counter on this host
  during the resolve, so inside the span. The total is the larger of the two
  readings, measured, never their sum. That is exact when the stage read the
  cascade's counter, and a lower bound if it read another; the cost carries no
  counter identity, so the cascade cannot tell.
- A live span and an estimated zero (a cache hit): the zero adds nothing, so
  the total is the span's reading alone and is measured. Every microjoule of
  it was read. Tagging it estimated would under-claim a real reading.

The eoc CLI test's hardware-counter branch asserted the old rule (measured
whenever the counter added anything). It now asserts the new one: the
backend's estimate plus the reading, tagged estimated. That branch does not run
on this Mac, where IOReport declines and the meter is the powermetrics stand-in
("eoc cost on this host: meter=powermetrics cost=50000000 µJ (estimated)").

Tests. The cascade is driven with a scripted counter that one test shares with
a self-metering neural backend, as a host's one counter would be. An estimate
plus a live 1,000 -> 1,500 span is `estimated(50_000_500)`, and its receipt
verifies. A stage that read 1,100 -> 1,400 inside 1,000 -> 1,500 costs
`measured(500)`, not 800. A cache hit inside a live 2,000 -> 2,250 span is
`measured(250)`. A counter that holds, fails its first read, or fails its last
leaves `estimated(42)` alone. Memoized and hand-inserted hits through a stub
cascade are `estimated(0)`. A table test covers every branch of the cost rule
and of the liveness check.

Falsified on the uncommitted tree. Each mutant was restored byte-for-byte and
re-touched so cargo rebuilt from the real source; a diff fingerprint taken
before and after matched:
- `zero()` back to `Measured` fails three tests, in eoc-core, eoc-cache and
  eoc-cascade;
- the cache stamping hits `measured(0)` fails the hand-inserted hit
  (`left: JouleCost { microjoules: 0, source: Measured }`);
- the memoizer stamping `measured(0)` fails the memoized hit;
- tagging estimate + span `Measured` fails with
  `left: JouleCost { microjoules: 50000500, source: Measured }`;
- summing a measured stage onto the span fails with `left: ... 800 ...`,
  `right: ... 500 ...`;
- HEAD's rule, verbatim, fails three of the new tests;
- counting a counter that held (`e >= s`) as live fails two.

eoc-bench, a separate workspace that builds against eoc-core, eoc-cache and
eoc-cascade, passes too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 20:17:08 -04:00
..
src eoc-cascade: a cache hit claims no measurement, and a total is measured only if every microjoule was read 2026-09-30 20:17:08 -04:00
Cargo.toml Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00