codecarbon 2.8.2__tar.gz → 3.0.0rc0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/.gitignore +2 -3
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/PKG-INFO +7 -1
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/README.md +6 -0
- codecarbon-3.0.0rc0/codecarbon/_version.py +1 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/cli/main.py +11 -5
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/api_client.py +2 -2
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/cpu.py +30 -3
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/emissions.py +2 -2
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/resource_tracker.py +77 -12
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/units.py +6 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/util.py +5 -3
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/hardware/cpu_power.csv +4 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/emissions_tracker.py +44 -5
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/external/hardware.py +89 -6
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/file.py +5 -7
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/pyproject.toml +2 -2
- codecarbon-2.8.2/codecarbon/_version.py +0 -1
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/LICENSE +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/cli/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/cli/cli_utils.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/cloud.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/co2_signal.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/config.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/gpu.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/measure.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/powermetrics.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/rapl.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/core/schemas.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/canada_provinces.geojson +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/cloud/impact.csv +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2016/canada_energy_mix.json +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2016/global_energy_mix-old.json +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2020/01_get_world_carbon_intensity.ipynb +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2020/02_convert_csv_to_json.ipynb +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2020/eu-carbon-intensity-electricity.csv +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2023-07-07-22-40-48.png +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/our_world_in_data.ipynb +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/world_energy_mix.csv +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/external/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/external/geography.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/external/logger.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/external/scheduler.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/external/task.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/input.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/lock.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/base_output.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/emissions_data.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/http.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/logger.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/metrics/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/metrics/logfire.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/output_methods/metrics/prometheus.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/assets/__init__.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/assets/car_icon.png +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/assets/house_icon.png +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/assets/tv_icon.png +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/carbonboard.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/carbonboard_on_api.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/components.py +0 -0
- {codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/viz/data.py +0 -0
|
@@ -8,11 +8,9 @@ __pycache__/
|
|
|
8
8
|
*intel_power_gadget_log.csv
|
|
9
9
|
*powermetrics_log.txt
|
|
10
10
|
!tests/test_data/mock*
|
|
11
|
-
.codecarbon.config
|
|
12
11
|
|
|
13
12
|
# C extensions
|
|
14
13
|
*.so
|
|
15
|
-
.codecarbon.config
|
|
16
14
|
|
|
17
15
|
# Distribution / packaging
|
|
18
16
|
.Python
|
|
@@ -132,4 +130,5 @@ tests/test_data/rapl/*
|
|
|
132
130
|
*.cast
|
|
133
131
|
|
|
134
132
|
# credentials
|
|
135
|
-
credentials
|
|
133
|
+
credentials*
|
|
134
|
+
.codecarbon.config*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecarbon
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0rc0
|
|
4
4
|
Project-URL: Homepage, https://codecarbon.io/
|
|
5
5
|
Project-URL: Repository, https://github.com/mlco2/codecarbon
|
|
6
6
|
Project-URL: Dashboard, http://dashboard.codecarbon.io/
|
|
@@ -212,3 +212,9 @@ Here is a sample for BibTeX:
|
|
|
212
212
|
# Contact 📝
|
|
213
213
|
|
|
214
214
|
Maintainers are [@vict0rsch](https://github.com/vict0rsch) [@benoit-cty](https://github.com/benoit-cty) and [@SaboniAmine](https://github.com/saboniamine). Codecarbon is developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
|
|
215
|
+
|
|
216
|
+
## Star History
|
|
217
|
+
|
|
218
|
+
Comparison of the number of stars accumulated by the different Python CO2 emissions projects:
|
|
219
|
+
[](https://star-history.com/#mlco2/codecarbon&lfwa/carbontracker&sb-ai-lab/Eco2AI&fvaleye/tracarbon&Breakend/experiment-impact-tracker&Date)
|
|
220
|
+
|
|
@@ -172,3 +172,9 @@ Here is a sample for BibTeX:
|
|
|
172
172
|
# Contact 📝
|
|
173
173
|
|
|
174
174
|
Maintainers are [@vict0rsch](https://github.com/vict0rsch) [@benoit-cty](https://github.com/benoit-cty) and [@SaboniAmine](https://github.com/saboniamine). Codecarbon is developed by volunteers from [**Mila**](http://mila.quebec) and the [**DataForGoodFR**](https://twitter.com/dataforgood_fr) community alongside donated professional time of engineers at [**Comet.ml**](https://comet.ml) and [**BCG GAMMA**](https://www.bcg.com/en-nl/beyond-consulting/bcg-gamma/default).
|
|
175
|
+
|
|
176
|
+
## Star History
|
|
177
|
+
|
|
178
|
+
Comparison of the number of stars accumulated by the different Python CO2 emissions projects:
|
|
179
|
+
[](https://star-history.com/#mlco2/codecarbon&lfwa/carbontracker&sb-ai-lab/Eco2AI&fvaleye/tracarbon&Breakend/experiment-impact-tracker&Date)
|
|
180
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.0.0_rc0"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sys
|
|
2
3
|
import time
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Optional
|
|
@@ -99,12 +100,14 @@ def show_config(path: Path = Path("./.codecarbon.config")) -> None:
|
|
|
99
100
|
)
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
def get_fief_auth():
|
|
104
|
+
fief = Fief(AUTH_SERVER_URL, AUTH_CLIENT_ID)
|
|
105
|
+
fief_auth = FiefAuth(fief, "./credentials.json")
|
|
106
|
+
return fief_auth
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
def _get_access_token():
|
|
107
|
-
access_token_info =
|
|
110
|
+
access_token_info = get_fief_auth().access_token_info()
|
|
108
111
|
access_token = access_token_info["access_token"]
|
|
109
112
|
return access_token
|
|
110
113
|
|
|
@@ -124,7 +127,7 @@ def api_get():
|
|
|
124
127
|
|
|
125
128
|
@codecarbon.command("login", short_help="Login to CodeCarbon")
|
|
126
129
|
def login():
|
|
127
|
-
|
|
130
|
+
get_fief_auth().authorize()
|
|
128
131
|
|
|
129
132
|
|
|
130
133
|
def get_api_key(project_id: str):
|
|
@@ -327,7 +330,10 @@ def monitor(
|
|
|
327
330
|
experiment_id = get_existing_local_exp_id()
|
|
328
331
|
if api:
|
|
329
332
|
if experiment_id is None:
|
|
330
|
-
print(
|
|
333
|
+
print(
|
|
334
|
+
"ERROR: No experiment id, call 'codecarbon config' first.",
|
|
335
|
+
file=sys.stderr,
|
|
336
|
+
)
|
|
331
337
|
print("CodeCarbon is going in an infinite loop to monitor this machine.")
|
|
332
338
|
with EmissionsTracker(
|
|
333
339
|
measure_power_secs=measure_power_secs,
|
|
@@ -257,8 +257,8 @@ class ApiClient: # (AsyncClient)
|
|
|
257
257
|
gpu_count=self.conf.get("gpu_count"),
|
|
258
258
|
gpu_model=self.conf.get("gpu_model"),
|
|
259
259
|
# Reduce precision for Privacy
|
|
260
|
-
longitude=round(self.conf.get("longitude"), 1),
|
|
261
|
-
latitude=round(self.conf.get("latitude"), 1),
|
|
260
|
+
longitude=round(self.conf.get("longitude", 0), 1),
|
|
261
|
+
latitude=round(self.conf.get("latitude", 0), 1),
|
|
262
262
|
region=self.conf.get("region"),
|
|
263
263
|
provider=self.conf.get("provider"),
|
|
264
264
|
ram_total_size=self.conf.get("ram_total_size"),
|
|
@@ -5,12 +5,14 @@ https://software.intel.com/content/www/us/en/develop/articles/intel-power-gadget
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
import shutil
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
11
12
|
from typing import Dict, Optional, Tuple
|
|
12
13
|
|
|
13
14
|
import pandas as pd
|
|
15
|
+
import psutil
|
|
14
16
|
from rapidfuzz import fuzz, process, utils
|
|
15
17
|
|
|
16
18
|
from codecarbon.core.rapl import RAPLFile
|
|
@@ -58,6 +60,21 @@ def is_rapl_available() -> bool:
|
|
|
58
60
|
return False
|
|
59
61
|
|
|
60
62
|
|
|
63
|
+
def is_psutil_available():
|
|
64
|
+
try:
|
|
65
|
+
nice = psutil.cpu_times().nice
|
|
66
|
+
if nice > 0.1:
|
|
67
|
+
return True
|
|
68
|
+
else:
|
|
69
|
+
return False
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.debug(
|
|
72
|
+
"Not using the psutil interface, an exception occurred while instantiating "
|
|
73
|
+
+ f"psutil.cpu_percent : {e}",
|
|
74
|
+
)
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
61
78
|
class IntelPowerGadget:
|
|
62
79
|
"""
|
|
63
80
|
A class to interface with Intel Power Gadget for monitoring CPU power consumption on Windows and (non-Apple Silicon) macOS.
|
|
@@ -294,7 +311,7 @@ class IntelRAPL:
|
|
|
294
311
|
logger.debug("We will read Intel RAPL files at %s", rapl_file)
|
|
295
312
|
except PermissionError as e:
|
|
296
313
|
raise PermissionError(
|
|
297
|
-
"Unable to read Intel RAPL files for CPU power, we will use a constant for your CPU power."
|
|
314
|
+
"PermissionError : Unable to read Intel RAPL files for CPU power, we will use a constant for your CPU power."
|
|
298
315
|
+ " Please view https://github.com/mlco2/codecarbon/issues/244"
|
|
299
316
|
+ " for workarounds : %s",
|
|
300
317
|
e,
|
|
@@ -332,8 +349,6 @@ class IntelRAPL:
|
|
|
332
349
|
"""
|
|
333
350
|
Return CPU details without computing them.
|
|
334
351
|
"""
|
|
335
|
-
logger.debug("get_static_cpu_details %s", self._cpu_details)
|
|
336
|
-
|
|
337
352
|
return self._cpu_details
|
|
338
353
|
|
|
339
354
|
def start(self) -> None:
|
|
@@ -426,6 +441,18 @@ class TDP:
|
|
|
426
441
|
start_cpu = model_raw.find(" CPU @ ")
|
|
427
442
|
if start_cpu > 0:
|
|
428
443
|
model_raw = model_raw[0:start_cpu]
|
|
444
|
+
model_raw = model_raw.replace(" CPU", "")
|
|
445
|
+
model_raw = re.sub(r" @\s*\d+\.\d+GHz", "", model_raw)
|
|
446
|
+
direct_match = process.extractOne(
|
|
447
|
+
model_raw,
|
|
448
|
+
cpu_df["Name"],
|
|
449
|
+
processor=lambda s: s.lower(),
|
|
450
|
+
scorer=fuzz.ratio,
|
|
451
|
+
score_cutoff=THRESHOLD_DIRECT,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
if direct_match:
|
|
455
|
+
return direct_match[0]
|
|
429
456
|
indirect_matches = process.extract(
|
|
430
457
|
model_raw,
|
|
431
458
|
cpu_df["Name"],
|
|
@@ -90,7 +90,7 @@ class Emissions:
|
|
|
90
90
|
selected = df.loc[flags]
|
|
91
91
|
if not len(selected):
|
|
92
92
|
raise ValueError(
|
|
93
|
-
"Unable to find country
|
|
93
|
+
"Unable to find country ISO Code for "
|
|
94
94
|
f"cloud_provider={cloud.provider}, "
|
|
95
95
|
f"cloud_region={cloud.region}"
|
|
96
96
|
)
|
|
@@ -105,7 +105,7 @@ class Emissions:
|
|
|
105
105
|
selected = df.loc[flags]
|
|
106
106
|
if not len(selected):
|
|
107
107
|
raise ValueError(
|
|
108
|
-
"Unable to find
|
|
108
|
+
"Unable to find State/City name for "
|
|
109
109
|
f"cloud_provider={cloud.provider}, "
|
|
110
110
|
f"cloud_region={cloud.region}"
|
|
111
111
|
)
|
|
@@ -4,7 +4,7 @@ from typing import List, Union
|
|
|
4
4
|
from codecarbon.core import cpu, gpu, powermetrics
|
|
5
5
|
from codecarbon.core.config import parse_gpu_ids
|
|
6
6
|
from codecarbon.core.util import detect_cpu_model, is_linux_os, is_mac_os, is_windows_os
|
|
7
|
-
from codecarbon.external.hardware import CPU, GPU, RAM, AppleSiliconChip
|
|
7
|
+
from codecarbon.external.hardware import CPU, GPU, MODE_CPU_LOAD, RAM, AppleSiliconChip
|
|
8
8
|
from codecarbon.external.logger import logger
|
|
9
9
|
|
|
10
10
|
|
|
@@ -23,6 +23,31 @@ class ResourceTracker:
|
|
|
23
23
|
|
|
24
24
|
def set_CPU_tracking(self):
|
|
25
25
|
logger.info("[setup] CPU Tracking...")
|
|
26
|
+
tdp = cpu.TDP()
|
|
27
|
+
if self.tracker._conf.get("force_mode_cpu_load", False) and tdp.tdp is not None:
|
|
28
|
+
if tdp.tdp is None:
|
|
29
|
+
logger.warning(
|
|
30
|
+
"Force CPU load mode requested but TDP could not be calculated. Falling back to another mode."
|
|
31
|
+
)
|
|
32
|
+
elif cpu.is_psutil_available():
|
|
33
|
+
# Register a CPU with MODE_CPU_LOAD
|
|
34
|
+
power = tdp.tdp
|
|
35
|
+
model = tdp.model
|
|
36
|
+
hardware = CPU.from_utils(
|
|
37
|
+
self.tracker._output_dir,
|
|
38
|
+
MODE_CPU_LOAD,
|
|
39
|
+
model,
|
|
40
|
+
power,
|
|
41
|
+
tracking_mode=self.tracker._tracking_mode,
|
|
42
|
+
)
|
|
43
|
+
self.cpu_tracker = MODE_CPU_LOAD
|
|
44
|
+
self.tracker._conf["cpu_model"] = hardware.get_model()
|
|
45
|
+
self.tracker._hardware.append(hardware)
|
|
46
|
+
return
|
|
47
|
+
else:
|
|
48
|
+
logger.warning(
|
|
49
|
+
"Force CPU load mode requested but psutil is not available."
|
|
50
|
+
)
|
|
26
51
|
if cpu.is_powergadget_available() and self.tracker._default_cpu_power is None:
|
|
27
52
|
logger.info("Tracking Intel CPU via Power Gadget")
|
|
28
53
|
self.cpu_tracker = "Power Gadget"
|
|
@@ -32,9 +57,15 @@ class ResourceTracker:
|
|
|
32
57
|
elif cpu.is_rapl_available():
|
|
33
58
|
logger.info("Tracking Intel CPU via RAPL interface")
|
|
34
59
|
self.cpu_tracker = "RAPL"
|
|
35
|
-
hardware = CPU.from_utils(
|
|
60
|
+
hardware = CPU.from_utils(
|
|
61
|
+
output_dir=self.tracker._output_dir, mode="intel_rapl"
|
|
62
|
+
)
|
|
36
63
|
self.tracker._hardware.append(hardware)
|
|
37
64
|
self.tracker._conf["cpu_model"] = hardware.get_model()
|
|
65
|
+
if "AMD Ryzen Threadripper" in self.tracker._conf["cpu_model"]:
|
|
66
|
+
logger.warning(
|
|
67
|
+
"The RAPL energy and power reported is divided by 2 for all 'AMD Ryzen Threadripper' as it seems to give better results."
|
|
68
|
+
)
|
|
38
69
|
# change code to check if powermetrics needs to be installed or just sudo setup
|
|
39
70
|
elif (
|
|
40
71
|
powermetrics.is_powermetrics_available()
|
|
@@ -72,7 +103,7 @@ class ResourceTracker:
|
|
|
72
103
|
elif is_windows_os():
|
|
73
104
|
cpu_tracking_install_instructions = "Windows OS detected: Please install Intel Power Gadget to measure CPU"
|
|
74
105
|
elif is_linux_os():
|
|
75
|
-
cpu_tracking_install_instructions = "Linux OS detected: Please ensure RAPL files exist at
|
|
106
|
+
cpu_tracking_install_instructions = "Linux OS detected: Please ensure RAPL files exist at /sys/class/powercap/intel-rapl to measure CPU"
|
|
76
107
|
logger.warning(
|
|
77
108
|
f"No CPU tracking mode found. Falling back on CPU constant mode. \n {cpu_tracking_install_instructions}\n"
|
|
78
109
|
)
|
|
@@ -89,17 +120,46 @@ class ResourceTracker:
|
|
|
89
120
|
logger.info(f"CPU Model on constant consumption mode: {model}")
|
|
90
121
|
self.tracker._conf["cpu_model"] = model
|
|
91
122
|
if tdp:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
123
|
+
if cpu.is_psutil_available():
|
|
124
|
+
logger.warning(
|
|
125
|
+
"No CPU tracking mode found. Falling back on CPU load mode."
|
|
126
|
+
)
|
|
127
|
+
hardware = CPU.from_utils(
|
|
128
|
+
self.tracker._output_dir,
|
|
129
|
+
MODE_CPU_LOAD,
|
|
130
|
+
model,
|
|
131
|
+
power,
|
|
132
|
+
tracking_mode=self.tracker._tracking_mode,
|
|
133
|
+
)
|
|
134
|
+
self.cpu_tracker = MODE_CPU_LOAD
|
|
135
|
+
else:
|
|
136
|
+
logger.warning(
|
|
137
|
+
"No CPU tracking mode found. Falling back on CPU constant mode."
|
|
138
|
+
)
|
|
139
|
+
hardware = CPU.from_utils(
|
|
140
|
+
self.tracker._output_dir, "constant", model, power
|
|
141
|
+
)
|
|
142
|
+
self.cpu_tracker = "global constant"
|
|
95
143
|
self.tracker._hardware.append(hardware)
|
|
96
144
|
else:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
145
|
+
if cpu.is_psutil_available():
|
|
146
|
+
logger.warning(
|
|
147
|
+
"Failed to match CPU TDP constant. Falling back on CPU load mode."
|
|
148
|
+
)
|
|
149
|
+
hardware = CPU.from_utils(
|
|
150
|
+
self.tracker._output_dir,
|
|
151
|
+
MODE_CPU_LOAD,
|
|
152
|
+
model,
|
|
153
|
+
power,
|
|
154
|
+
tracking_mode=self.tracker._tracking_mode,
|
|
155
|
+
)
|
|
156
|
+
self.cpu_tracker = MODE_CPU_LOAD
|
|
157
|
+
else:
|
|
158
|
+
logger.warning(
|
|
159
|
+
"Failed to match CPU TDP constant. Falling back on a global constant."
|
|
160
|
+
)
|
|
161
|
+
self.cpu_tracker = "global constant"
|
|
162
|
+
hardware = CPU.from_utils(self.tracker._output_dir, "constant")
|
|
103
163
|
self.tracker._hardware.append(hardware)
|
|
104
164
|
|
|
105
165
|
def set_GPU_tracking(self):
|
|
@@ -130,10 +190,15 @@ class ResourceTracker:
|
|
|
130
190
|
self.tracker._conf["gpu_count"] = len(
|
|
131
191
|
gpu_devices.devices.get_gpu_static_info()
|
|
132
192
|
)
|
|
193
|
+
self.gpu_tracker = "pynvml"
|
|
133
194
|
else:
|
|
134
195
|
logger.info("No GPU found.")
|
|
135
196
|
|
|
136
197
|
def set_CPU_GPU_ram_tracking(self):
|
|
198
|
+
"""
|
|
199
|
+
Set up CPU, GPU and RAM tracking based on the user's configuration.
|
|
200
|
+
param tracker: BaseEmissionsTracker object
|
|
201
|
+
"""
|
|
137
202
|
self.set_RAM_tracking()
|
|
138
203
|
self.set_CPU_tracking()
|
|
139
204
|
self.set_GPU_tracking()
|
|
@@ -145,3 +145,9 @@ class Power:
|
|
|
145
145
|
|
|
146
146
|
def __mul__(self, factor: float) -> "Power":
|
|
147
147
|
return Power(self.kW * factor)
|
|
148
|
+
|
|
149
|
+
def __truediv__(self, divisor: float) -> "Power":
|
|
150
|
+
return Power(self.kW / divisor)
|
|
151
|
+
|
|
152
|
+
def __floordiv__(self, divisor: float) -> "Power":
|
|
153
|
+
return Power(self.kW // divisor)
|
|
@@ -48,6 +48,8 @@ def resolve_path(path: Union[str, Path]) -> Path:
|
|
|
48
48
|
def backup(file_path: Union[str, Path], ext: Optional[str] = ".bak") -> None:
|
|
49
49
|
"""
|
|
50
50
|
Resolves the path to a path then backs it up, adding the extension provided.
|
|
51
|
+
Warning : this function will rename the file in place, it's the calling function that will write a new file at the original path.
|
|
52
|
+
This function will not overwrite existing backups but add a number.
|
|
51
53
|
|
|
52
54
|
Args:
|
|
53
55
|
file_path (Union[str, Path]): Path to a file to backup.
|
|
@@ -111,7 +113,7 @@ def count_cpus() -> int:
|
|
|
111
113
|
"Error running `scontrol show job $SLURM_JOB_ID` "
|
|
112
114
|
+ "to count SLURM-available cpus. Using the machine's cpu count."
|
|
113
115
|
)
|
|
114
|
-
return psutil.cpu_count()
|
|
116
|
+
return psutil.cpu_count(logical=True)
|
|
115
117
|
|
|
116
118
|
num_cpus_matches = re.findall(r"NumCPUs=\d+", scontrol)
|
|
117
119
|
|
|
@@ -120,14 +122,14 @@ def count_cpus() -> int:
|
|
|
120
122
|
"Could not find NumCPUs= after running `scontrol show job $SLURM_JOB_ID` "
|
|
121
123
|
+ "to count SLURM-available cpus. Using the machine's cpu count."
|
|
122
124
|
)
|
|
123
|
-
return psutil.cpu_count()
|
|
125
|
+
return psutil.cpu_count(logical=True)
|
|
124
126
|
|
|
125
127
|
if len(num_cpus_matches) > 1:
|
|
126
128
|
logger.warning(
|
|
127
129
|
"Unexpected output after running `scontrol show job $SLURM_JOB_ID` "
|
|
128
130
|
+ "to count SLURM-available cpus. Using the machine's cpu count."
|
|
129
131
|
)
|
|
130
|
-
return psutil.cpu_count()
|
|
132
|
+
return psutil.cpu_count(logical=True)
|
|
131
133
|
|
|
132
134
|
num_cpus = num_cpus_matches[0].replace("NumCPUs=", "")
|
|
133
135
|
logger.debug(f"Detected {num_cpus} cpus available on SLURM.")
|
|
@@ -563,6 +563,7 @@ AMD EPYC 7F32,180
|
|
|
563
563
|
AMD EPYC 7F52,240
|
|
564
564
|
AMD EPYC 7F72,240
|
|
565
565
|
AMD EPYC 7H12,280
|
|
566
|
+
AMD EPYC 8024P,90
|
|
566
567
|
AMD EPYC Embedded 3251,50
|
|
567
568
|
AMD FX-4100,95
|
|
568
569
|
AMD FX-4120,95
|
|
@@ -2231,6 +2232,7 @@ Intel Core i7-1185G7E,28
|
|
|
2231
2232
|
Intel Core i7-1185GRE,28
|
|
2232
2233
|
Intel Core i7-1195G7,28
|
|
2233
2234
|
Intel Core i7-1270P,64
|
|
2235
|
+
Intel Core i7-12700K,190
|
|
2234
2236
|
Intel Core i7-1360P,28
|
|
2235
2237
|
Intel Core i7-2600,95
|
|
2236
2238
|
Intel Core i7-2600K,95
|
|
@@ -3188,6 +3190,7 @@ Intel Xeon E5-2618L v2,50
|
|
|
3188
3190
|
Intel Xeon E5-2618L v4,75
|
|
3189
3191
|
Intel Xeon E5-2620,95
|
|
3190
3192
|
Intel Xeon E5-2620 v2,80
|
|
3193
|
+
Intel Xeon E5-2620 v3,85
|
|
3191
3194
|
Intel Xeon E5-2620 v4,85
|
|
3192
3195
|
Intel Xeon E5-2623 v4,85
|
|
3193
3196
|
Intel Xeon E5-2628L,60
|
|
@@ -3751,6 +3754,7 @@ Intel Xeon Platinum 8376HL,205
|
|
|
3751
3754
|
Intel Xeon Platinum 8380,270
|
|
3752
3755
|
Intel Xeon Platinum 8380H,250
|
|
3753
3756
|
Intel Xeon Platinum 8380HL,250
|
|
3757
|
+
Intel Xeon Platinum 8370C,205
|
|
3754
3758
|
Intel Xeon Platinum 9221,250
|
|
3755
3759
|
Intel Xeon Platinum 9222,250
|
|
3756
3760
|
Intel Xeon Platinum 9242,350
|
|
@@ -64,6 +64,9 @@ class BaseEmissionsTracker(ABC):
|
|
|
64
64
|
and `CarbonTracker.`
|
|
65
65
|
"""
|
|
66
66
|
|
|
67
|
+
_scheduler: Optional[PeriodicScheduler] = None
|
|
68
|
+
_scheduler_monitor_power: Optional[PeriodicScheduler] = None
|
|
69
|
+
|
|
67
70
|
def _set_from_conf(
|
|
68
71
|
self, var, name, default=None, return_type=None, prevent_setter=False
|
|
69
72
|
):
|
|
@@ -170,6 +173,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
170
173
|
logger_preamble: Optional[str] = _sentinel,
|
|
171
174
|
default_cpu_power: Optional[int] = _sentinel,
|
|
172
175
|
pue: Optional[int] = _sentinel,
|
|
176
|
+
force_mode_cpu_load: Optional[bool] = _sentinel,
|
|
173
177
|
allow_multiple_runs: Optional[bool] = _sentinel,
|
|
174
178
|
):
|
|
175
179
|
"""
|
|
@@ -225,6 +229,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
225
229
|
messages. Defaults to "".
|
|
226
230
|
:param default_cpu_power: cpu power to be used as default if the cpu is not known.
|
|
227
231
|
:param pue: PUE (Power Usage Effectiveness) of the datacenter.
|
|
232
|
+
:param force_mode_cpu_load: Force the addition of a CPU in MODE_CPU_LOAD
|
|
228
233
|
:param allow_multiple_runs: Allow multiple instances of codecarbon running in parallel. Defaults to False.
|
|
229
234
|
"""
|
|
230
235
|
|
|
@@ -274,6 +279,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
274
279
|
self._set_from_conf(logger_preamble, "logger_preamble", "")
|
|
275
280
|
self._set_from_conf(default_cpu_power, "default_cpu_power")
|
|
276
281
|
self._set_from_conf(pue, "pue", 1.0, float)
|
|
282
|
+
self._set_from_conf(force_mode_cpu_load, "force_mode_cpu_load", False)
|
|
277
283
|
self._set_from_conf(
|
|
278
284
|
experiment_id, "experiment_id", "5b0fa12a-3dd7-45bb-9766-cc326314d9f1"
|
|
279
285
|
)
|
|
@@ -330,6 +336,10 @@ class BaseEmissionsTracker(ABC):
|
|
|
330
336
|
function=self._measure_power_and_energy,
|
|
331
337
|
interval=self._measure_power_secs,
|
|
332
338
|
)
|
|
339
|
+
self._scheduler_monitor_power = PeriodicScheduler(
|
|
340
|
+
function=self._monitor_power,
|
|
341
|
+
interval=1,
|
|
342
|
+
)
|
|
333
343
|
|
|
334
344
|
self._data_source = DataSource()
|
|
335
345
|
|
|
@@ -417,6 +427,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
417
427
|
hardware.start()
|
|
418
428
|
|
|
419
429
|
self._scheduler.start()
|
|
430
|
+
self._scheduler_monitor_power.start()
|
|
420
431
|
|
|
421
432
|
def start_task(self, task_name=None) -> None:
|
|
422
433
|
"""
|
|
@@ -428,6 +439,9 @@ class BaseEmissionsTracker(ABC):
|
|
|
428
439
|
if self._scheduler:
|
|
429
440
|
self._scheduler.stop()
|
|
430
441
|
|
|
442
|
+
# Task background thread for measuring power
|
|
443
|
+
self._scheduler_monitor_power.start()
|
|
444
|
+
|
|
431
445
|
if self._active_task:
|
|
432
446
|
logger.info("A task is already under measure")
|
|
433
447
|
return
|
|
@@ -457,6 +471,9 @@ class BaseEmissionsTracker(ABC):
|
|
|
457
471
|
emissions.
|
|
458
472
|
:return: None
|
|
459
473
|
"""
|
|
474
|
+
if self._scheduler_monitor_power:
|
|
475
|
+
self._scheduler_monitor_power.stop()
|
|
476
|
+
|
|
460
477
|
task_name = task_name if task_name else self._active_task
|
|
461
478
|
self._measure_power_and_energy()
|
|
462
479
|
|
|
@@ -524,6 +541,9 @@ class BaseEmissionsTracker(ABC):
|
|
|
524
541
|
if self._scheduler:
|
|
525
542
|
self._scheduler.stop()
|
|
526
543
|
self._scheduler = None
|
|
544
|
+
if self._scheduler_monitor_power:
|
|
545
|
+
self._scheduler_monitor_power.stop()
|
|
546
|
+
self._scheduler_monitor_power = None
|
|
527
547
|
else:
|
|
528
548
|
logger.warning("Tracker already stopped !")
|
|
529
549
|
for task_name in self._tasks:
|
|
@@ -652,6 +672,17 @@ class BaseEmissionsTracker(ABC):
|
|
|
652
672
|
:return: Metadata containing cloud info
|
|
653
673
|
"""
|
|
654
674
|
|
|
675
|
+
def _monitor_power(self) -> None:
|
|
676
|
+
"""
|
|
677
|
+
Monitor the power consumption of the hardware.
|
|
678
|
+
We do this for hardware that does not support energy monitoring.
|
|
679
|
+
So we could average the power consumption.
|
|
680
|
+
This method is called every 1 second. Even if we are in Task mode.
|
|
681
|
+
"""
|
|
682
|
+
for hardware in self._hardware:
|
|
683
|
+
if isinstance(hardware, CPU):
|
|
684
|
+
hardware.monitor_power()
|
|
685
|
+
|
|
655
686
|
def _do_measurements(self) -> None:
|
|
656
687
|
for hardware in self._hardware:
|
|
657
688
|
h_time = time.perf_counter()
|
|
@@ -668,8 +699,11 @@ class BaseEmissionsTracker(ABC):
|
|
|
668
699
|
self._total_cpu_energy += energy
|
|
669
700
|
self._cpu_power = power
|
|
670
701
|
logger.info(
|
|
671
|
-
f"
|
|
672
|
-
+ f"
|
|
702
|
+
f"Delta energy consumed for CPU with {hardware._mode} : {energy.kWh:.6f} kWh"
|
|
703
|
+
+ f", power : {self._cpu_power.W} W"
|
|
704
|
+
)
|
|
705
|
+
logger.info(
|
|
706
|
+
f"Energy consumed for All CPU : {self._total_cpu_energy.kWh:.6f} kWh"
|
|
673
707
|
)
|
|
674
708
|
elif isinstance(hardware, GPU):
|
|
675
709
|
self._total_gpu_energy += energy
|
|
@@ -704,8 +738,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
704
738
|
logger.error(f"Unknown hardware type: {hardware} ({type(hardware)})")
|
|
705
739
|
h_time = time.perf_counter() - h_time
|
|
706
740
|
logger.debug(
|
|
707
|
-
f"{hardware.__class__.__name__} : {
|
|
708
|
-
+ f"W during {last_duration:,.2f} s [measurement time: {h_time:,.4f}]"
|
|
741
|
+
f"Done measure for {hardware.__class__.__name__} - measurement time: {h_time:,.4f} s - last call {last_duration:,.2f} s"
|
|
709
742
|
)
|
|
710
743
|
logger.info(
|
|
711
744
|
f"{self._total_energy.kWh:.6f} kWh of electricity used since the beginning."
|
|
@@ -717,7 +750,13 @@ class BaseEmissionsTracker(ABC):
|
|
|
717
750
|
every `self._measure_power_secs` seconds.
|
|
718
751
|
:return: None
|
|
719
752
|
"""
|
|
720
|
-
|
|
753
|
+
try:
|
|
754
|
+
last_duration = time.perf_counter() - self._last_measured_time
|
|
755
|
+
except AttributeError as e:
|
|
756
|
+
logger.debug(
|
|
757
|
+
f"You need to start the tracker first before measuring. Or maybe you do multiple run at the same time ? Error: {e}"
|
|
758
|
+
)
|
|
759
|
+
raise e
|
|
721
760
|
|
|
722
761
|
warning_duration = self._measure_power_secs * 3
|
|
723
762
|
if last_duration > warning_duration:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Encapsulates external dependencies to retrieve hardware metadata
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import math
|
|
5
6
|
import re
|
|
6
7
|
import subprocess
|
|
7
8
|
from abc import ABC, abstractmethod
|
|
@@ -14,7 +15,7 @@ from codecarbon.core.cpu import IntelPowerGadget, IntelRAPL
|
|
|
14
15
|
from codecarbon.core.gpu import AllGPUDevices
|
|
15
16
|
from codecarbon.core.powermetrics import ApplePowermetrics
|
|
16
17
|
from codecarbon.core.units import Energy, Power, Time
|
|
17
|
-
from codecarbon.core.util import SLURM_JOB_ID, detect_cpu_model
|
|
18
|
+
from codecarbon.core.util import SLURM_JOB_ID, count_cpus, detect_cpu_model
|
|
18
19
|
from codecarbon.external.logger import logger
|
|
19
20
|
|
|
20
21
|
# default W value for a CPU if no model is found in the ref csv
|
|
@@ -25,6 +26,8 @@ CONSUMPTION_PERCENTAGE_CONSTANT = 0.5
|
|
|
25
26
|
|
|
26
27
|
B_TO_GB = 1024 * 1024 * 1024
|
|
27
28
|
|
|
29
|
+
MODE_CPU_LOAD = "cpu_load"
|
|
30
|
+
|
|
28
31
|
|
|
29
32
|
@dataclass
|
|
30
33
|
class BaseHardware(ABC):
|
|
@@ -147,12 +150,20 @@ class CPU(BaseHardware):
|
|
|
147
150
|
model: str,
|
|
148
151
|
tdp: int,
|
|
149
152
|
rapl_dir: str = "/sys/class/powercap/intel-rapl",
|
|
153
|
+
tracking_mode: str = "machine",
|
|
150
154
|
):
|
|
155
|
+
assert tracking_mode in ["machine", "process"]
|
|
156
|
+
self._power_history: List[Power] = []
|
|
151
157
|
self._output_dir = output_dir
|
|
152
158
|
self._mode = mode
|
|
153
159
|
self._model = model
|
|
154
160
|
self._tdp = tdp
|
|
155
161
|
self._is_generic_tdp = False
|
|
162
|
+
self._tracking_mode = tracking_mode
|
|
163
|
+
self._pid = psutil.Process().pid
|
|
164
|
+
self._cpu_count = count_cpus()
|
|
165
|
+
self._process = psutil.Process(self._pid)
|
|
166
|
+
|
|
156
167
|
if self._mode == "intel_power_gadget":
|
|
157
168
|
self._intel_interface = IntelPowerGadget(self._output_dir)
|
|
158
169
|
elif self._mode == "intel_rapl":
|
|
@@ -169,12 +180,62 @@ class CPU(BaseHardware):
|
|
|
169
180
|
|
|
170
181
|
return s + ")"
|
|
171
182
|
|
|
183
|
+
@staticmethod
|
|
184
|
+
def _calculate_power_from_cpu_load(tdp, cpu_load, model):
|
|
185
|
+
if "AMD Ryzen Threadripper" in model:
|
|
186
|
+
return CPU._calculate_power_from_cpu_load_treadripper(tdp, cpu_load)
|
|
187
|
+
else:
|
|
188
|
+
return tdp * (cpu_load / 100.0)
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _calculate_power_from_cpu_load_treadripper(tdp, cpu_load):
|
|
192
|
+
load = cpu_load / 100.0
|
|
193
|
+
|
|
194
|
+
if load < 0.1: # Below 10% CPU load
|
|
195
|
+
return tdp * (0.05 * load * 10)
|
|
196
|
+
elif load <= 0.3: # 10-30% load - linear phase
|
|
197
|
+
return tdp * (0.05 + 1.8 * (load - 0.1))
|
|
198
|
+
elif load <= 0.5: # 30-50% load - adjusted coefficients
|
|
199
|
+
# Increased base power and adjusted curve
|
|
200
|
+
base_power = 0.45 # Increased from 0.41
|
|
201
|
+
power_range = 0.50 # Increased from 0.44
|
|
202
|
+
factor = ((load - 0.3) / 0.2) ** 1.8 # Reduced power from 2.0 to 1.8
|
|
203
|
+
return tdp * (base_power + power_range * factor)
|
|
204
|
+
else: # Above 50% - plateau phase
|
|
205
|
+
return tdp * (0.85 + 0.15 * (1 - math.exp(-(load - 0.5) * 5)))
|
|
206
|
+
|
|
207
|
+
def _get_power_from_cpu_load(self):
|
|
208
|
+
"""
|
|
209
|
+
When in MODE_CPU_LOAD
|
|
210
|
+
"""
|
|
211
|
+
if self._tracking_mode == "machine":
|
|
212
|
+
tdp = self._tdp
|
|
213
|
+
cpu_load = psutil.cpu_percent(interval=0.5, percpu=False)
|
|
214
|
+
power = self._calculate_power_from_cpu_load(tdp, cpu_load, self._model)
|
|
215
|
+
logger.debug(
|
|
216
|
+
f"A TDP of {self._tdp} W and a CPU load of {cpu_load:.1f}% give an estimation of {power} W for whole machine."
|
|
217
|
+
)
|
|
218
|
+
elif self._tracking_mode == "process":
|
|
219
|
+
cpu_load = (
|
|
220
|
+
self._process.cpu_percent(interval=0.5, percpu=False) / self._cpu_count
|
|
221
|
+
)
|
|
222
|
+
power = self._calculate_power_from_cpu_load(self.tdp, cpu_load, self._model)
|
|
223
|
+
logger.debug(
|
|
224
|
+
f"A TDP of {self._tdp} W and a CPU load of {cpu_load * 100:.1f}% give an estimation of {power} W for process {self._pid}."
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
raise Exception(f"Unknown tracking_mode {self._tracking_mode}")
|
|
228
|
+
return Power.from_watts(power)
|
|
229
|
+
|
|
172
230
|
def _get_power_from_cpus(self) -> Power:
|
|
173
231
|
"""
|
|
174
232
|
Get CPU power
|
|
175
233
|
:return: power in kW
|
|
176
234
|
"""
|
|
177
|
-
if self._mode ==
|
|
235
|
+
if self._mode == MODE_CPU_LOAD:
|
|
236
|
+
power = self._get_power_from_cpu_load()
|
|
237
|
+
return power
|
|
238
|
+
elif self._mode == "constant":
|
|
178
239
|
power = self._tdp * CONSUMPTION_PERCENTAGE_CONSTANT
|
|
179
240
|
return Power.from_watts(power)
|
|
180
241
|
if self._mode == "intel_rapl":
|
|
@@ -208,20 +269,35 @@ class CPU(BaseHardware):
|
|
|
208
269
|
return Energy.from_energy(energy)
|
|
209
270
|
|
|
210
271
|
def total_power(self) -> Power:
|
|
211
|
-
|
|
212
|
-
|
|
272
|
+
self._power_history.append(self._get_power_from_cpus())
|
|
273
|
+
power_history_in_W = [power.W for power in self._power_history]
|
|
274
|
+
cpu_power = sum(power_history_in_W) / len(power_history_in_W)
|
|
275
|
+
self._power_history = []
|
|
276
|
+
return Power.from_watts(cpu_power)
|
|
213
277
|
|
|
214
278
|
def measure_power_and_energy(self, last_duration: float) -> Tuple[Power, Energy]:
|
|
215
279
|
if self._mode == "intel_rapl":
|
|
216
280
|
energy = self._get_energy_from_cpus(delay=Time(seconds=last_duration))
|
|
217
281
|
power = self.total_power()
|
|
282
|
+
# Patch AMD Threadripper that count 2x the power
|
|
283
|
+
if "AMD Ryzen Threadripper" in self._model:
|
|
284
|
+
power = power / 2
|
|
285
|
+
energy = energy / 2
|
|
218
286
|
return power, energy
|
|
219
|
-
# If not intel_rapl
|
|
287
|
+
# If not intel_rapl, we call the parent method from BaseHardware
|
|
288
|
+
# to compute energy from power and time
|
|
220
289
|
return super().measure_power_and_energy(last_duration=last_duration)
|
|
221
290
|
|
|
222
291
|
def start(self):
|
|
223
292
|
if self._mode in ["intel_power_gadget", "intel_rapl", "apple_powermetrics"]:
|
|
224
293
|
self._intel_interface.start()
|
|
294
|
+
if self._mode == MODE_CPU_LOAD:
|
|
295
|
+
# The first time this is called it will return a meaningless 0.0 value which you are supposed to ignore.
|
|
296
|
+
_ = self._get_power_from_cpu_load()
|
|
297
|
+
|
|
298
|
+
def monitor_power(self):
|
|
299
|
+
cpu_power = self._get_power_from_cpus()
|
|
300
|
+
self._power_history.append(cpu_power)
|
|
225
301
|
|
|
226
302
|
def get_model(self):
|
|
227
303
|
return self._model
|
|
@@ -233,6 +309,7 @@ class CPU(BaseHardware):
|
|
|
233
309
|
mode: str,
|
|
234
310
|
model: Optional[str] = None,
|
|
235
311
|
tdp: Optional[int] = None,
|
|
312
|
+
tracking_mode: str = "machine",
|
|
236
313
|
) -> "CPU":
|
|
237
314
|
if model is None:
|
|
238
315
|
model = detect_cpu_model()
|
|
@@ -245,7 +322,13 @@ class CPU(BaseHardware):
|
|
|
245
322
|
cpu._is_generic_tdp = True
|
|
246
323
|
return cpu
|
|
247
324
|
|
|
248
|
-
return cls(
|
|
325
|
+
return cls(
|
|
326
|
+
output_dir=output_dir,
|
|
327
|
+
mode=mode,
|
|
328
|
+
model=model,
|
|
329
|
+
tdp=tdp,
|
|
330
|
+
tracking_mode=tracking_mode,
|
|
331
|
+
)
|
|
249
332
|
|
|
250
333
|
|
|
251
334
|
@dataclass
|
|
@@ -78,12 +78,10 @@ class FileOutput(BaseOutput):
|
|
|
78
78
|
self.output_dir, "emissions_" + experiment_name + "_" + run_id + ".csv"
|
|
79
79
|
)
|
|
80
80
|
df = pd.DataFrame(columns=data[0].values.keys())
|
|
81
|
-
|
|
82
|
-
[
|
|
83
|
-
df,
|
|
84
|
-
pd.DataFrame.from_records(
|
|
85
|
-
[dict(data_point.values) for data_point in data]
|
|
86
|
-
),
|
|
87
|
-
]
|
|
81
|
+
new_df = pd.DataFrame.from_records(
|
|
82
|
+
[dict(data_point.values) for data_point in data]
|
|
88
83
|
)
|
|
84
|
+
# Filter out empty or all-NA columns, to avoid warnings from Pandas
|
|
85
|
+
new_df = new_df.dropna(axis=1, how="all")
|
|
86
|
+
df = pd.concat([df, new_df], ignore_index=True)
|
|
89
87
|
df.to_csv(save_task_file_path, index=False)
|
|
@@ -183,8 +183,8 @@ include = [
|
|
|
183
183
|
]
|
|
184
184
|
|
|
185
185
|
[tool.bumpver]
|
|
186
|
-
current_version = "
|
|
187
|
-
version_pattern = "MAJOR.MINOR.PATCH"
|
|
186
|
+
current_version = "3.0.0_rc0"
|
|
187
|
+
version_pattern = "MAJOR.MINOR.PATCH[_TAGNUM]"
|
|
188
188
|
|
|
189
189
|
[tool.bumpver.file_patterns]
|
|
190
190
|
"codecarbon/_version.py" = [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.8.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2016/canada_energy_mix.json
RENAMED
|
File without changes
|
|
File without changes
|
{codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2016/usa_emissions.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
{codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/2023-07-07-22-40-48.png
RENAMED
|
File without changes
|
|
File without changes
|
{codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/global_energy_mix.json
RENAMED
|
File without changes
|
{codecarbon-2.8.2 → codecarbon-3.0.0rc0}/codecarbon/data/private_infra/our_world_in_data.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|