jouleclaw: the NVML sampling floor is ~100 ms, measured, not ~50 ms #316

Merged
dcharlot merged 1 commit from fix/jouleclaw-nvml-sampling-floor into main 2026-10-18 13:44:41 -04:00

2026-10-18

Transaction Science
jouleclaw: the NVML sampling floor is ~100 ms, measured, not ~50 ms
All checks were successful
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Successful in 39s
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 12m22s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 58s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 21s
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 11s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m19s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 8m28s
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Successful in 18m21s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 3m10s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m4s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m20s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 3m22s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 4m32s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 3m6s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 2m13s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 4m42s
SPEC §1.3 published a normative floor for the NVML cumulative-energy
counter — "~1 mJ, ~50 ms. `HwShunt`." — feeding the MUST three lines
above it: "Implementations MUST surface `resolution_uj()` and
`min_window_ns()` so callers can refuse to claim microjoule accuracy on
platforms that cannot deliver it." `min_window_ns()` returned
50_000_000, and the figure was restated in three more places.

Dauner et al., *Evaluating the Influence of Measurement Frequency on
Energy Readings Using Intel RAPL and NVIDIA NVML*, ACM SIGENERGY Energy
Informatics Review 6(2), July 2026, measured it across 588 controlled
runs: the cumulative counter **does not update faster than roughly
100 ms on any device they tested**. The stale-read fraction is 93.4 % at
0.5 ms, 86.3 % at 10 ms, 1.0 % at 100 ms, and zero from 500 ms.

So 50 ms does not merely understate the floor — it sits *inside* the
stale-read region, where a read mostly returns the preceding value. We
graded that `HwShunt`, our strongest provenance. A counter read below
its own update rate is not a reading of the span; that is the rule this
family exists to enforce, and our own SPEC broke it.

Two numbers, kept apart on purpose:

- `min_window_ns()` is documented as "the smallest sampling window below
  which readings are noise-dominated" — the counter's UPDATE period.
  That is what the paper measures at ~100 ms, and what this raises it
  to. It is load-bearing, not advisory: `jouledesk-github`'s
  `window_energy` refuses a figure for work shorter than it.
- Accuracy against integrated power above that floor is a DIFFERENT
  property and device-class dependent — an A100 agreed to ±0.2 % at
  every interval, an unvalidated RTX 4090 was still 13.3 % low at 100 ms
  and within 1.6 % only at 1 s. Folding that into the same scalar would
  repeat the category error being fixed, so it is stated in SPEC §1.3
  prose, with the paper's own recommendation: on an unvalidated counter
  prefer the power integral — which `Mode::PowerIntegral` already is,
  graded `ModelBased`.

`PROBE_TICK` also moves, 100 ms → 150 ms, and this is the sharper bug.
Its rationale read "two of its ~50 ms sample periods"; against a real
~100 ms period the tick EQUALLED the update period, so roughly half the
intervals catch no update, the probe misses `LIVE_SHARE` (four in five)
and grades a perfectly healthy counter not-live — demoting a real
`HwShunt` reading to the power integral. 150 ms matches
`sandbox-meter`'s `NVML_PROBE_TICK`, whose comment already carried the
right derivation, and `MIN_LIVE_ADVANCES` (5) intervals then take 750 ms,
inside the 1000 ms `LIVENESS_BUDGET`.

`the_probe_tick_clears_the_update_period_and_fits_the_budget` pins both
halves; nothing pinned either before.

Not changed: the RAPL floor. The same paper finds RAPL stable at 10 ms
or coarser, which is what SPEC §1.3 already says.

Verified with the feature on — `cargo test -p jouleclaw-energy --features
rapl,nvml`, 84 passed — because the `nvml` module is not in a default
build. `tests/nvml_hardware.rs` ran 0 tests here: it needs a real GPU and
only exercises on the `linux-hw` runner.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-18 07:27:24 -04:00