energy meters: Linux RAPL and NVML on real hardware; an NVML counter past its board is Implausible #129

Merged
dcharlot merged 10 commits from energy/meters-on-real-linux-hardware into main 2026-10-01 18:33:48 -04:00

2026-10-01

Transaction Science
energy meters: document the hardware tests' helper functions
Some checks failed
WAI energy receipts / energy receipts · linux (pull_request) Successful in 39s
wai formal verification (integer core) / kani (pull_request) Successful in 3m56s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 53s
WAI neural decoders end-to-end / synthetic fixtures → Rust decode → reference (±4) (pull_request) Successful in 1m4s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 25s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 2m33s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 21m57s
standards workspaces build + test / aac-rs (pull_request) Successful in 8m24s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 4m4s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 2m34s
standards workspaces build + test / map-rs (pull_request) Successful in 4m15s
standards workspaces build + test / mesh-rs (pull_request) Successful in 2m34s
standards workspaces build + test / openpay (pull_request) Successful in 15m42s
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Successful in 1m25s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 5m21s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 1m2s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m58s
standards workspaces build + test / arl-rs (pull_request) Successful in 3m19s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 5m44s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 7m23s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 9m0s
standards workspaces build + test / sense-rs (pull_request) Successful in 6m7s
standards workspaces build + test / macOS energy backend (jcp-energy) (pull_request) Successful in 7m49s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 1m0s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 4m51s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 56s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 2m22s
wai cross-architecture byte-equality / windows-x86_64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · windows (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · windows (pull_request) Has been cancelled
jouleclaw's public-surface audit failed #129 on seven helpers in
jouleclaw-energy's rapl_hardware.rs that had no doc comment: the
bracket's zone sums, the masked view, and the watch summary. Document
them, and the same helpers in the other crates' copies of the module, so
the copies stay alike.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
jouleclaw-energy: the GPU plausibility rule is dead code without nvml, and says so
A build without the nvml feature (the workspace's own cargo check) warned
that GpuWatch, plausible_gpu_counter and its constants are never used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
proof-cascade: the RAPL hardware test takes 0 as the crate's 'inside the idle noise'
A mutation run on jetson-hub while its CI jobs held the package near its
power limit (load average 11) read 0 µJ a call through both windows: the
busy window drew no more than the idle one, which the crate documents as a
bracket's 'inside the idle noise', not a defect. The test asserted a
non-zero figure; it now logs the load and keeps the assertions that hold
whatever the load: the calls' total within the oracle's package-only
energy around the measurement, the HwShunt grade and the package domain.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
eoc-meter: the GPU counter is never the cascade's counter, and one past its board is Implausible
Two defects of eoc's NVML path (feature cuda), both seen on jetson-hub's
RTX 4050 Laptop GPU (driver 595.84).

detect() ranked NVML first, so on a host with a live NVML counter every
consumer (eoc-cascade, eoc-local, eoc-streaming, eoc-agent) was handed the
GPU's counter and graded its advance Measured, with no coverage field to
say the CPU's share of the work went unread: on jetson-hub detect()
returned nvml over a live RAPL. By the family's rule a figure that does not
cover the work it is booked to is an estimate in a format without coverage.
detect() now has no NVML rung, and NvmlCounter is no longer a JouleCounter
(read_gpu_microjoules() reads it as GPU telemetry), so no consumer can grade
it Measured. JouleSource and the README say so.

And the counter does not count the board's energy while it is polled: at
the probe's 100 ms wait it ran at 81-136 W while the board drew 13-15 W
against a 65 W maximum limit. The probe now reads the board's draw with
each sample and its maximum power limit once (gpu_plausibility), and a
live counter past 1.5x the limit, past twice the mean draw plus 5 W, or
beside a draw reading past 1.5x the limit, is CounterStatus::Implausible
and NvmlCounter is not constructed.

Before: tests/nvml_hardware.rs failed on jetson-hub (status=Live against an
oracle verdict of Implausible, 119.4 W counter, 13-13.5 W draw;
detect()=nvml). After: status=Implausible, constructed=false,
detect()=rapl; 58 lib tests with cuda (57 without), the NVML and RAPL
hardware tests pass there with and without cuda.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
jouleclaw-energy: an NVML counter that runs past its board is Implausible, not HwShunt
On jetson-hub's RTX 4050 Laptop GPU (driver 595.84) the cumulative-energy
counter advances in every 100 ms probe interval, so the counter was Live
and every reading HwShunt over Gpu. But it does not count the board's
energy while it is polled: at the probe's 100 ms tick it ran at 127-305 W
while the board drew 5.5-13.4 W against a 65 W maximum limit, and a 1 s
span read 44.8 W of HwShunt.

The probe now reads every GPU's power draw with each sample and its
maximum power limit once (liveness::GpuWatch), and a live counter one of
whose GPUs ran past 1.5x its limit, past twice its board's mean draw plus
5 W, or beside a draw reading itself past 1.5x the limit, is
CounterLiveness::Implausible: the counter falls to the power-draw integral,
graded ModelBased, as for a counter that is not live. A bound whose reading
is missing is not applied. The module doc's 50 ms republication period is
corrected to the ~100 ms measured.

Before: tests/nvml_hardware.rs failed on jetson-hub, liveness=Live against
an oracle verdict of Implausible (304.5 W counter, 5.5-7.7 W draw). After:
liveness=Implausible, grade ModelBased; 56 lib tests (the rule's among
them) and the NVML, RAPL and conformance tests pass there with
--features rapl,nvml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
jcp-energy: an NVML counter that runs past its board is Implausible, not Live
On jetson-hub's RTX 4050 Laptop GPU (driver 595.84) the total-energy
counter advances in every 100 ms probe interval, so nvml_status() was Live
and the NVML rung would grade its spans HwShunt (Partial). But the counter
does not count the board's energy while it is polled: at the probe's 100 ms
wait it ran at 72-136 W while the board drew 12-14 W against a 65 W maximum
limit.

The probe now reads the board's power draw with every sample and its
maximum power limit once, and a live counter that ran past 1.5x the limit,
past twice the board's mean draw plus 5 W, or beside a draw reading itself
past 1.5x the limit (590 W in the first second after the GPU wakes) is
CounterStatus::Implausible, which no rung admits. A bound whose reading is
missing is not applied.

Before: tests/nvml_hardware.rs failed on jetson-hub, status=Live against an
oracle verdict of Implausible (135.8 W counter, 13.5 W draw). After:
status=Implausible; 32 lib tests (the rule's among them), the NVML and RAPL
hardware tests pass there with --features nvml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
sandbox-meter: an NVML counter that runs past its board is Implausible, not Live
On jetson-hub's RTX 4050 Laptop GPU (driver 595.84) the total-energy
counter is republished every ~100 ms and advances in every probe interval,
so the probe called it Live and every span carried a Gpu record graded
HwShunt. But the counter does not count the board's energy while it is
polled: at the probe's 150 ms tick it ran at 44-103 W while the board drew
10-14 W against a 65 W maximum limit (304 W at a 100 ms tick; another
process polling every 10 ms drove it to 415 W in an unpolled reader's
span), and a 1 s span with the meter's two reads came out at 22-31 W.

The probe now reads each GPU's power draw with every sample and its
maximum power limit once, and a live run whose counter ran past 1.5x the
limit, past twice the board's mean draw plus 5 W (NVML's stated draw
accuracy), or whose draw reading itself was past 1.5x the limit (590 W for
the first second after the GPU wakes) is CounterStatus::Implausible: the
NVML probe is not built, and the meter has no GPU reading. A bound whose
reading is missing is not applied.

Before: tests/nvml_hardware.rs failed on jetson-hub, status=Live against an
oracle verdict of Implausible (57.8 W counter, 13.1-13.6 W draw). After:
status=Implausible, sources [rapl-powercap]; 104 lib tests (the new rule and
run tests among them), the NVML and RAPL hardware tests and
sandbox-exec-microvm's pass there.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
energy meters: NVML hardware tests against an oracle of their own
eoc-meter (cuda), sandbox-meter, jcp-energy (nvml) and jouleclaw-energy
(nvml) each get a test binary that opens libnvidia-ml itself (dlopen and
its own symbols, not the crate's bindings), measures how often the
total-energy counter is republished (about every 100 ms on jetson-hub's
RTX 4050 Laptop, driver 595.84), samples it at the crate's own tick by the
sustained-advance rule, and holds it to its board through nvidia-smi: no
faster than 1.5x the maximum power limit, the board's draw within that
bound too, and no faster than twice the draw plus 5 W. The status must
agree with that verdict; a live counter's figure must be graded as the
family's rule says for a GPU-only figure.

On jetson-hub all four fail as committed: the counter advances in every
interval, so each crate calls it Live and grades it as hardware, but polled
at the probes' 100-150 ms ticks it runs at 58-305 W while the board draws
5.5-13.6 W (max limit 65 W). The fixes follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
jcp-energy: the fresh-process pin expects only the probes the ladder reached
On a Linux host whose RAPL is live (jetson-hub, i9-13900H) the ladder
selects RAPL and never asks NVML or the per-process counter, so with the
nvml feature the pin's 'NVML probe ran once' failed there: 'the NVML
liveness probe ran 0 times' (left 0, right 1). The expectation now follows
the detection's own record of which statuses were asked. Run on
jetson-hub: 30 lib tests pass with and without nvml (29 + this one failed
before).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00
Transaction Science
energy meters: Linux RAPL hardware tests against an oracle of their own
Each of the five meter crates (and the S5 executor's power meters) gets a
test binary, cfg(target_os = "linux"), that reads /sys/class/powercap
itself -- every zone's name, energy_uj and max_energy_range_uj -- and
applies the sustained-advance rule to the top-level package-<n> zones on
its own. The crate's RAPL status must agree with that verdict; where live,
the meter's figure over a CPU burn must sit inside the oracle's
package-only bracket (outer and inner reads around the meter's), below
package+psys and package+core wherever the bracket can resolve them, and be
graded as the crate's hardware grade with honest coverage. Where the
counters cannot be read the status must say so; the CI container's view
(Docker masks /sys/devices/virtual/powercap) is replayed where the crate
takes a root.

Run on jetson-hub (i9-13900H: intel-rapl:0 package-0, :0:0 core,
:1 psys, intel-rapl-mmio:0 root-only): all live, all inside the bracket.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 10:18:29 -04:00