The Windows EMI backend detects package channels that mirror the same underlying energy counter and drops the duplicates. The Linux powercap path does not, and sums them. If the premise behind the Windows guard is correct, CodeCarbon overstates CPU power on multi-die AMD parts (Threadripper, EPYC) on Linux by a factor equal to the die count.
I want to be upfront that the last step here needs hardware to settle — but the codebase currently asserts both positions, so one of them is wrong.
The two paths
Windows deliberately discards mirrored per-die channels. From codecarbon/core/windows_emi.py:310-324:
Multi-die CPUs (e.g. AMD Ryzen Threadripper / EPYC) expose one package channel per die, but every die mirrors the same socket-wide RAPL counter. Summing those channels multiplies the reported CPU power by the number of dies, exactly like the package-0-die-0 / package-0-die-1 duplicates seen through the Linux powercap interface.
That docstring names the Linux case directly and calls it the same defect. The guard was added deliberately and recently (17c78568 "Multi-die fix for Windows EMI", 15a73945 "Fix multiple-counting").
Linux sums them. _classify_domains (codecarbon/core/cpu.py:648-649) puts any domain whose name contains package into package_domains:
if "package" in domain_lower:
package_domains.append(domain_tuple)
_deduplicate_domains (cpu.py:746-761) then keys the map on domain_name. Since package-0-die-0 and package-0-die-1 are distinct names, both survive deduplication and both are summed. There is no mirrored-counter check anywhere on the Linux path.
Supporting evidence
docs/explanation/rapl.md contains a worked Threadripper example whose summary table reads:
package-0-die-0: 68 W | package-0-die-1: 68 W | CodeCarbon: 137 W
Two dies reporting an identical 68 W, summed to 137 W. Under the Windows docstring premise that is the double-count, observed on real hardware — the documentation has been presenting the symptom as correct behaviour.
Impact if confirmed
CPU power roughly doubled on 2-die parts and worse on higher die counts, for the exact hardware class most likely to be running large training jobs on Linux. It would be silent: no warning, and a plausible-looking number. It would also affect every downstream figure, since CPU energy feeds total energy and emissions.
What needs settling
Whether package-0-die-0 and package-0-die-1 in Linux powercap report independent per-die counters or mirrors of one socket-wide counter. This is a hardware question and may vary by CPU generation and kernel version. Reading identical values on a loaded machine is strong evidence of mirroring, which is the heuristic the Windows path already uses (_counters_match, windows_emi.py:305-307).
If they mirror, the fix is to port the Windows mirrored-channel detection to the Linux domain selection rather than reimplementing it. If they are genuinely independent, then the Windows guard is over-aggressive and is under-reporting on Windows, and the docstring claim about Linux should be corrected.
Either way the two backends should not disagree about the same hardware.
Note
Documentation PRs currently in flight touch rapl.md, including the Threadripper walkthrough. Whichever way this is resolved, that page should stop presenting 68 + 68 → 137 W as confirmed-correct behaviour until it is.
The Windows EMI backend detects package channels that mirror the same underlying energy counter and drops the duplicates. The Linux powercap path does not, and sums them. If the premise behind the Windows guard is correct, CodeCarbon overstates CPU power on multi-die AMD parts (Threadripper, EPYC) on Linux by a factor equal to the die count.
I want to be upfront that the last step here needs hardware to settle — but the codebase currently asserts both positions, so one of them is wrong.
The two paths
Windows deliberately discards mirrored per-die channels. From
codecarbon/core/windows_emi.py:310-324:That docstring names the Linux case directly and calls it the same defect. The guard was added deliberately and recently (
17c78568"Multi-die fix for Windows EMI",15a73945"Fix multiple-counting").Linux sums them.
_classify_domains(codecarbon/core/cpu.py:648-649) puts any domain whose name containspackageintopackage_domains:_deduplicate_domains(cpu.py:746-761) then keys the map ondomain_name. Sincepackage-0-die-0andpackage-0-die-1are distinct names, both survive deduplication and both are summed. There is no mirrored-counter check anywhere on the Linux path.Supporting evidence
docs/explanation/rapl.mdcontains a worked Threadripper example whose summary table reads:Two dies reporting an identical 68 W, summed to 137 W. Under the Windows docstring premise that is the double-count, observed on real hardware — the documentation has been presenting the symptom as correct behaviour.
Impact if confirmed
CPU power roughly doubled on 2-die parts and worse on higher die counts, for the exact hardware class most likely to be running large training jobs on Linux. It would be silent: no warning, and a plausible-looking number. It would also affect every downstream figure, since CPU energy feeds total energy and emissions.
What needs settling
Whether
package-0-die-0andpackage-0-die-1in Linux powercap report independent per-die counters or mirrors of one socket-wide counter. This is a hardware question and may vary by CPU generation and kernel version. Reading identical values on a loaded machine is strong evidence of mirroring, which is the heuristic the Windows path already uses (_counters_match,windows_emi.py:305-307).If they mirror, the fix is to port the Windows mirrored-channel detection to the Linux domain selection rather than reimplementing it. If they are genuinely independent, then the Windows guard is over-aggressive and is under-reporting on Windows, and the docstring claim about Linux should be corrected.
Either way the two backends should not disagree about the same hardware.
Note
Documentation PRs currently in flight touch
rapl.md, including the Threadripper walkthrough. Whichever way this is resolved, that page should stop presenting68 + 68 → 137 Was confirmed-correct behaviour until it is.