sandbox-meter + jcp-gateway: count, and flag, work services did on the tree's behalf #214
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/tree-delegated-energy"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
The process-tree meter (#198) reads a server's own CPU energy. On this kernel, work a system service does on a process's behalf is billed to that process in fields any same-user process can read:
ri_billed_energyandri_serviced_energy, in nJ;ri_billed_system_timeandri_serviced_system_time, in mach ticks.The bill is not part of the process's own count. A probe measured:
Until now that delegated work was invisible, and only the operator's "self-contained" declaration stood against it.
What
TreeMeter.Delegated(any bill moved);Serviced(any serviced count moved);ServicedBeyondSpent(a member's net went negative).delegated,servicedandunsettled-at-startare the gaps that can put a figure over the call's energy as well as under it, for example a late bill for earlier work landing in this span. The docs say so.Partialeven when declared self-contained: evidence overrides the declaration. The declaration still gatesWholewhen no bill arrives.gap_kindhas no catch-all, so a new gap kind fails to compile until it is named.detect_in_process).proc_pidinfoflavourPROC_PIDTHREADCOUNTS, which the SDK exposes only in a private header. The probe is built only where it reads in the expected layout, and a host where it is absent gets estimates only.child_reading/Attribution::child_energy_ujare documented as lower bounds.joule-metermeters its own workload in-process.Wholeonly for work that hands nothing to another process, and that a self-read MUST check its fold. The sandbox README says the same.Evidence
Delegatedgap fires, the bill is at least the oracle's, and figure minus bill equals the oracle's own advance to the nanojoule. Pure-CPU calls on the same server read whole and exact. In the gateway, a declared server's delegating calls are sealedHwShunt+Partialwith["delegated"].Ten mutation checks each broke their target test.
Not covered
Measurementhas no coverage field to carry it.🤖 Generated with Claude Code
The kernel's bank ledgers bill CPU work a system service does on a process's behalf to that process (ri_billed_energy / ri_billed_system_time), and credit the servicer (ri_serviced_*). Measured on this M5 Max: exactly 0 in every CPU-only control, 4-45x a client's own energy for on-device model inference, ~200x for a metadata search, ~2.4x for speech. The tree meter read none of it, so a server that delegated its work read a plausible own share with nothing to show the rest. Each member's count is now ri_energy_nj - ri_serviced_energy + ri_billed_energy, its advances summed signed: work a member did for others is theirs, work others did for it is the tree's, and service between two members is counted once, through the payer. A member whose net goes below zero (a credit for earlier service landing in the span) is counted as it is and is a gap, TreeGap::ServicedBeyondSpent, never a wrapped u64; a sum not above zero is no reading. Any billed energy or CPU time in the span is TreeGap::Delegated { billed_nj, billed_cpu }: the bill is IN the figure and a lower bound of the delegated work (no GPU or Neural Engine energy, no unbilled threads, no late credits, nothing billed to another originator). The billed counts take no part in settling or in confirming a walk: another process's thread credits them, whatever the member is doing, up to a second and more late, so waiting on them would stretch every span without making a figure whole. The serviced counts stay in settling: the member's own thread moves them. A CPU-only tree's figure is unchanged (its ledgers are 0). Tests: the arithmetic (billed added, serviced subtracted, intra-tree service counted once, a negative member net a gap and summed signed, a born member's whole ledgers, a ledger that went back, billed CPU alone), settling with a bill landing between two reads, the rusage layout at 216/224/264/272, and a real server compiled at test time whose directory-service lookups are billed to it: Delegated fires, its bill is at least the raw-buffer oracle's, and the figure less the bill is the server's own advance to the nJ; its pure-CPU control and the awk control read whole and exact. Co-Authored-By: Claude Opus 5.5 <[email protected]>The tree figure subtracts each member's serviced credit (work it did for other processes, part of its own count) and raised a gap only when a member's net went below zero; a positive net read Whole. But a servicing thread is credited at its next voucher change, not as it works — a daemon's serviced energy was 14-100% of its own energy over a window in which its serviced CPU time was 93-99% — so a credit inside a span need not be that span's service: one for work before the span subtracts energy the span never held (short), and work in the span credited after the stop stays in (over). A reading graded Whole could be off either way. TreeGap::Serviced { serviced_nj, serviced_cpu } is now raised whenever a counted member's serviced energy or CPU time moved over the span, however the net came out; its docs, the TreeGap and module docs, SPEC §5.1 and the sandbox README name it, beside unsettled-at-start, as a gap that can put the figure OVER the span's energy as well as under. jcp-gateway names it `serviced` (the compiler now insists every kind has a name), and its docs say a `serviced` figure may be over. The unit test that asserted a serviced member reads whole now asserts the gap. Co-Authored-By: Claude Opus 5.5 <[email protected]>