Add support for Windows Energy Meter Interface (EMI) - #1263
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1263 +/- ##
==========================================
+ Coverage 89.70% 90.39% +0.69%
==========================================
Files 48 49 +1
Lines 4778 5103 +325
==========================================
+ Hits 4286 4613 +327
+ Misses 492 490 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Tested on Windows 11: |
09326ca to
f0152ee
Compare
|
New test done on a desktop computer with a smart plug:
Delta smart plug: 510-190=320W So I think we have the same problem of double counting that we have under Linux with RAPL with this CPU. |
rapl_include_dram for EMI — hardware_cache.py:132 only reads the DRAM/psys settings when hw._mode == "intel_rapl". A cached windows_emi CPU is rebuilt with rapl_include_dram=False, silently dropping the user's setting on the second run. Also, WindowsEMI stores it as emi_include_dram, so even adding the mode wouldn't match the getattr(intel, "rapl_include_dram") lookup.
… windows_emi.py
find_mirrored_channels returns a {channel: mirrored_reference} mapping instead of a bare list, so each candidate keeps a link to the channel it duplicates.
start() only flags candidates (_mirrored_candidates) and leaves them out of the reported energy; it no longer prunes _devices.
_confirm_mirrored_channels() runs at the start of each get_cpu_details(), before the deltas are computed:
equal deltas over the interval → permanently dropped (_drop_channels);
diverging deltas → the channel is a real independent meter and is restored (logged at INFO);
both deltas zero → inconclusive, stays pending for the next interval.
Net effect: a dual-socket machine whose two counters happen to coincide at the initial snapshot recovers its second socket after one interval, while multi-die duplicates are still excluded from the very first measurement (no over-count regression).
|
On my CPU it was not just double-counting, but multiple-counting: Windows exposes 16 EMI devices on this CPU, one per core:
The mirrored-counter detection never fired because these are genuinely distinct counters. That hypothesis was wrong for Windows (it stays valid for the Linux per-die case, which is unrelated). Fix Measured on my machine, at 100 % CPU load:
151 W under the 180 W TDP, instead of 1.55× above it. |
Description
Adds a new CPU power-measurement backend for Windows: the built-in Windows Energy Meter Interface (EMI), available since Windows 11. EMI exposes the same RAPL energy counters CodeCarbon already reads on Linux (
/sys/class/powercap), but through a standard OS device interface — no third-party driver, no administrator rights, and no extra Python dependency.Changes:
codecarbon/core/windows_emi.py: a stdlib-onlyctypesimplementation (WindowsEMIclass +is_emi_available()probe) that enumeratesGUID_DEVICE_ENERGY_METERdevices, reads their metadata/channels, and computes energy/power deltas from the cumulative picowatt-hour counters. Mirrors the existingIntelRAPLsemantics: package channels are preferred over PP0/PP1 subdomains to avoid double-counting, with an opt-inrapl_include_dramflag.codecarbon/external/hardware.py: new"windows_emi"CPU mode, treated as an energy-delta backend alongside"intel_rapl".codecarbon/core/resource_tracker.py: on Windows, CodeCarbon now tries EMI first, then falls back to Intel Power Gadget (kept for backward compatibility on machines where it's already installed), then to CPU-load estimation.codecarbon/core/hardware_cache.py: clears the EMI availability cache alongside the other hardware caches.docs/explanation/methodology.md: documents the new Windows measurement path and its limitations.tests/test_windows_emi.py(parsing, channel selection, energy/power computation, error handling, availability probe), plus updates totests/test_resource_tracker.py,tests/test_cpu_load.py, andtests/test_tracking_inference.pyto reflect the new backend priority.Related Issue
Resolves #457
Motivation and Context
Intel Power Gadget — CodeCarbon's only real (non-estimated) CPU power source on Windows — was discontinued by Intel in December 2023 and can no longer be downloaded, so in practice every Windows user has been silently falling back to TDP × CPU-load estimation for over two years.
The issue originally proposed Intel PCM as a replacement, but it's Intel-only and notoriously hard to install (kernel driver, admin rights, no pip package) — unlikely to ever be installed by an actual CodeCarbon user. A follow-up comment proposed LibreHardwareMonitor / PyHardwareMonitor, which does work for both Intel and AMD, but still requires administrator privileges and ships the WinRing0 kernel driver (which has known CVEs and is increasingly blocked by AV/EDR and Microsoft's vulnerable-driver blocklist), plus a pythonnet + .NET Framework dependency.
Windows 11 already exposes the same RAPL counters natively through the Energy Meter Interface — the same mechanism Firefox's power profiler switched to after Power Gadget's deprecation (Mozilla bug 1774844). It works for Intel and AMD, requires no admin rights, no kernel driver, and no extra dependency (validated: reads succeed from an unelevated process). This lets CodeCarbon get real hardware measurements on Windows again with zero added friction for users.
Limitations, documented in the methodology page: EMI requires Windows 11 on bare metal (Windows 10 only reports on devices with dedicated metering hardware, e.g. Surface Book); it won't work inside VMs, same as RAPL on Linux. Where it isn't available, CodeCarbon transparently falls back to Power Gadget (if installed) and then CPU-load estimation, so there's no regression for any existing setup.
How Has This Been Tested?
tests/test_windows_emi.py: unit tests for EMI metadata/channel parsing (V1 and V2), measurement parsing, package-channel selection logic (including multi-package and DRAM-inclusion cases),WindowsEMIsetup/energy computation (including negative-counter-delta and zero-time-delta edge cases), andis_emi_available()caching behavior — all via mocks, no real hardware access required.tests/test_resource_tracker.py,tests/test_cpu_load.py, andtests/test_tracking_inference.pyfor the new Windows backend priority (EMI before Power Gadget) and to account for a coarser counter-refresh granularity (<=instead of<for one assertion).RAPL_Package0_PKG/DRAM/PP0/PP1channels while running unelevated (~4 W idle). Then ran a fullEmissionsTrackersession under synthetic CPU load and confirmedCPU Tracking Method: Windows EMIwas selected automatically, with power readings around 15 W and correctly computed emissions.pytest tests, excluding modules gated on unrelated optional dependencies not installed in this environment —dash,joserfc) to confirm no regressions: all passing.black,isort, andflake8per the project's pre-commit config.Screenshots (if appropriate):
N/A (backend/library change, no UI impact).
Types of changes
AI Usage Disclosure
Please refer to docs/how-to/ai-policy.md for detailed guidelines on how to disclose AI usage in your PR. Accurately completing this section is mandatory.
Checklist:
Go over all the following points, and put an
xin all the boxes that apply.