Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions codecarbon/core/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from __future__ import annotations

import math
import os
import re
import shutil
Expand Down Expand Up @@ -872,8 +873,30 @@ def start(self) -> None:
"""
Starts monitoring CPU energy consumption.
"""
retained_rapl_files = []
observed_energies = []
for rapl_file in self._rapl_files:
rapl_file.start()
energy = float(rapl_file.last_energy)
is_dram = "dram" in rapl_file.name.lower()
is_mirrored = (
energy
and not is_dram
and any(
math.isclose(energy, observed_energy, rel_tol=1e-6)
for observed_energy in observed_energies
)
)
if is_mirrored:
logger.warning(
"\tRAPL - Ignoring mirrored energy counter at %s to avoid double-counting",
rapl_file.path,
)
continue
retained_rapl_files.append(rapl_file)
if energy and not is_dram:
observed_energies.append(energy)
self._rapl_files = retained_rapl_files


class TDP:
Expand Down
4 changes: 4 additions & 0 deletions docs/explanation/methodology.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ information.
Despite the name "Intel RAPL", it supports AMD processors since Linux
kernel 5.8.

On multi-die CPUs, package domains can mirror the same socket-wide energy
counter. CodeCarbon detects matching nonzero counters at startup and keeps one
of them, preventing CPU energy from being counted once per die.

Read more about how we use it in [RAPL Metrics](rapl.md).

## CPU metrics priority
Expand Down
2 changes: 1 addition & 1 deletion docs/explanation/rapl.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ and consistent measurements:
- `core` reports very low energy values
- Unclear if `core` is included in `package` (vendor documentation is
sparse)
- Multiple dies may report as separate packages (e.g., Threadripper)
- On some multi-die CPUs (e.g., Threadripper), separate package domains mirror the same socket-wide counter. CodeCarbon keeps one mirrored counter so CPU energy is not multiplied by the number of dies.

**What RAPL Does NOT Measure**:

Expand Down
40 changes: 40 additions & 0 deletions tests/test_rapl_mmio_scanning.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,46 @@
from codecarbon.core.cpu import IntelRAPL, is_rapl_available


@pytest.mark.parametrize(
("energies", "expected_count"),
[((1000000, 1000000), 1), ((1000000, 2000000), 2), ((0, 0), 2)],
)
def test_rapl_start_deduplicates_only_nonzero_mirrored_package_counters(
tmp_path, monkeypatch, energies, expected_count
):
monkeypatch.setattr(sys, "platform", "linux")
provider = tmp_path / "intel-rapl"
provider.mkdir()
for index, energy in enumerate(energies):
domain = provider / f"intel-rapl:{index}"
domain.mkdir()
(domain / "name").write_text(f"package-{index}-die-0")
(domain / "energy_uj").write_text(str(energy))
(domain / "max_energy_range_uj").write_text("262143328850")

rapl = IntelRAPL(rapl_dir=str(tmp_path))
rapl.start()

assert len(rapl._rapl_files) == expected_count


def test_rapl_start_keeps_dram_when_it_matches_a_package_counter(tmp_path, monkeypatch):
monkeypatch.setattr(sys, "platform", "linux")
provider = tmp_path / "intel-rapl"
provider.mkdir()
for index, name in enumerate(("package-0-die-0", "package-0-die-1", "dram")):
domain = provider / f"intel-rapl:{index}"
domain.mkdir()
(domain / "name").write_text(name)
(domain / "energy_uj").write_text("1000000")
(domain / "max_energy_range_uj").write_text("262143328850")

rapl = IntelRAPL(rapl_dir=str(tmp_path), rapl_include_dram=True)
rapl.start()

assert len(rapl._rapl_files) == 2


@pytest.mark.skipif(not sys.platform.lower().startswith("lin"), reason="requires Linux")
def test_multiple_rapl_providers_with_mixed_permissions(tmp_path):
"""
Expand Down
Loading