jouleclaw: the NVML sampling floor is ~100 ms, measured, not ~50 ms #316
Merged
dcharlot
merged 1 commit from 2026-10-18 13:44:41 -04:00
fix/jouleclaw-nvml-sampling-floor into main 2026-10-18
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]>