codecarbon 2.6.0__tar.gz → 2.7.1__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.6.0 → codecarbon-2.7.1}/PKG-INFO +1 -1
- codecarbon-2.7.1/codecarbon/_version.py +1 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/api_client.py +0 -3
- codecarbon-2.7.1/codecarbon/core/co2_signal.py +62 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/cpu.py +117 -29
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/gpu.py +38 -21
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/measure.py +5 -5
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/powermetrics.py +41 -22
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/rapl.py +0 -3
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/util.py +16 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/hardware/cpu_power.csv +1 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/emissions_tracker.py +137 -42
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/task.py +1 -1
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/input.py +0 -1
- codecarbon-2.7.1/codecarbon/lock.py +46 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/emissions_data.py +2 -1
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/file.py +2 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/pyproject.toml +16 -3
- codecarbon-2.6.0/codecarbon/_version.py +0 -1
- codecarbon-2.6.0/codecarbon/core/co2_signal.py +0 -35
- {codecarbon-2.6.0 → codecarbon-2.7.1}/.gitignore +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/LICENSE +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/README.md +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/cli/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/cli/cli_utils.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/cli/main.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/cloud.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/config.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/emissions.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/schemas.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/units.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/canada_provinces.geojson +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/cloud/impact.csv +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2016/canada_energy_mix.json +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2016/global_energy_mix-old.json +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/01_get_world_carbon_intensity.ipynb +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/02_convert_csv_to_json.ipynb +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/eu-carbon-intensity-electricity.csv +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2023-07-07-22-40-48.png +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/our_world_in_data.ipynb +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/world_energy_mix.csv +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/geography.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/hardware.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/logger.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/scheduler.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/base_output.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/http.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/logger.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/logfire.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/prometheus.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/__init__.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/car_icon.png +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/house_icon.png +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/tv_icon.png +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/carbonboard.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/carbonboard_on_api.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/components.py +0 -0
- {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/data.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.7.1"
|
|
@@ -8,7 +8,6 @@ TODO : use async call to API
|
|
|
8
8
|
# from httpx import AsyncClient
|
|
9
9
|
import dataclasses
|
|
10
10
|
import json
|
|
11
|
-
import time
|
|
12
11
|
from datetime import timedelta, tzinfo
|
|
13
12
|
|
|
14
13
|
import arrow
|
|
@@ -37,7 +36,6 @@ class ApiClient: # (AsyncClient)
|
|
|
37
36
|
"""
|
|
38
37
|
|
|
39
38
|
run_id = None
|
|
40
|
-
_previous_call = time.time()
|
|
41
39
|
|
|
42
40
|
def __init__(
|
|
43
41
|
self,
|
|
@@ -167,7 +165,6 @@ class ApiClient: # (AsyncClient)
|
|
|
167
165
|
|
|
168
166
|
def add_emission(self, carbon_emission: dict):
|
|
169
167
|
assert self.experiment_id is not None
|
|
170
|
-
self._previous_call = time.time()
|
|
171
168
|
if self.run_id is None:
|
|
172
169
|
# TODO : raise an Exception ?
|
|
173
170
|
logger.debug(
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from codecarbon.core.units import EmissionsPerKWh, Energy
|
|
6
|
+
from codecarbon.external.geography import GeoMetadata
|
|
7
|
+
|
|
8
|
+
URL: str = "https://api.co2signal.com/v1/latest"
|
|
9
|
+
CO2_SIGNAL_API_TIMEOUT: int = 30
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_emissions(
|
|
13
|
+
energy: Energy, geo: GeoMetadata, co2_signal_api_token: str = ""
|
|
14
|
+
) -> float:
|
|
15
|
+
"""
|
|
16
|
+
Calculate the CO2 emissions based on energy consumption and geographic location.
|
|
17
|
+
|
|
18
|
+
This function retrieves the carbon intensity (in grams of CO2 per kWh) from the CO2
|
|
19
|
+
Signal API based on the geographic location provided. It then calculates the total
|
|
20
|
+
CO2 emissions for a given amount of energy consumption.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
energy (Energy):
|
|
24
|
+
An object representing the energy consumption in kilowatt-hours (kWh).
|
|
25
|
+
geo (GeoMetadata):
|
|
26
|
+
Geographic metadata, including either latitude/longitude
|
|
27
|
+
or a country code.
|
|
28
|
+
co2_signal_api_token (str, optional):
|
|
29
|
+
The API token for authenticating with the CO2 Signal API (default is an empty string).
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
float:
|
|
33
|
+
The total CO2 emissions in kilograms based on the provided energy consumption and
|
|
34
|
+
carbon intensity of the specified geographic location.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
CO2SignalAPIError:
|
|
38
|
+
If the CO2 Signal API request fails or returns an error.
|
|
39
|
+
"""
|
|
40
|
+
params: Dict[str, Any]
|
|
41
|
+
if geo.latitude:
|
|
42
|
+
params = {"lat": geo.latitude, "lon": geo.longitude}
|
|
43
|
+
else:
|
|
44
|
+
params = {"countryCode": geo.country_2letter_iso_code}
|
|
45
|
+
resp = requests.get(
|
|
46
|
+
URL,
|
|
47
|
+
params=params,
|
|
48
|
+
headers={"auth-token": co2_signal_api_token},
|
|
49
|
+
timeout=CO2_SIGNAL_API_TIMEOUT,
|
|
50
|
+
)
|
|
51
|
+
if resp.status_code != 200:
|
|
52
|
+
message = resp.json().get("error") or resp.json().get("message")
|
|
53
|
+
raise CO2SignalAPIError(message)
|
|
54
|
+
carbon_intensity_g_per_kWh = resp.json()["data"]["carbonIntensity"]
|
|
55
|
+
emissions_per_kWh: EmissionsPerKWh = EmissionsPerKWh.from_g_per_kWh(
|
|
56
|
+
carbon_intensity_g_per_kWh
|
|
57
|
+
)
|
|
58
|
+
return emissions_per_kWh.kgs_per_kWh * energy.kWh
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CO2SignalAPIError(Exception):
|
|
62
|
+
pass
|
|
@@ -21,30 +21,60 @@ from codecarbon.input import DataSource
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def is_powergadget_available() -> bool:
|
|
24
|
+
"""
|
|
25
|
+
Checks if Intel Power Gadget is available on the system.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
bool: `True` if Intel Power Gadget is available, `False` otherwise.
|
|
29
|
+
"""
|
|
24
30
|
try:
|
|
25
31
|
IntelPowerGadget()
|
|
26
32
|
return True
|
|
27
33
|
except Exception as e:
|
|
28
34
|
logger.debug(
|
|
29
|
-
"Not using PowerGadget, an exception occurred while instantiating"
|
|
30
|
-
+
|
|
35
|
+
"Not using PowerGadget, an exception occurred while instantiating "
|
|
36
|
+
+ "IntelPowerGadget : %s",
|
|
37
|
+
e,
|
|
31
38
|
)
|
|
32
39
|
return False
|
|
33
40
|
|
|
34
41
|
|
|
35
42
|
def is_rapl_available() -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Checks if Intel RAPL is available on the system.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
bool: `True` if Intel RAPL is available, `False` otherwise.
|
|
48
|
+
"""
|
|
36
49
|
try:
|
|
37
50
|
IntelRAPL()
|
|
38
51
|
return True
|
|
39
52
|
except Exception as e:
|
|
40
53
|
logger.debug(
|
|
41
54
|
"Not using the RAPL interface, an exception occurred while instantiating "
|
|
42
|
-
+
|
|
55
|
+
+ "IntelRAPL : %s",
|
|
56
|
+
e,
|
|
43
57
|
)
|
|
44
58
|
return False
|
|
45
59
|
|
|
46
60
|
|
|
47
61
|
class IntelPowerGadget:
|
|
62
|
+
"""
|
|
63
|
+
A class to interface with Intel Power Gadget for monitoring CPU power consumption on Windows and (non-Apple Silicon) macOS.
|
|
64
|
+
|
|
65
|
+
This class provides methods to set up and execute Intel Power Gadget's command-line interface (CLI) to
|
|
66
|
+
log power consumption data over a specified duration and resolution. It also includes functionality to
|
|
67
|
+
read and process the logged data to extract CPU power details.
|
|
68
|
+
|
|
69
|
+
Methods:
|
|
70
|
+
start():
|
|
71
|
+
Placeholder method for starting the Intel Power Gadget monitoring.
|
|
72
|
+
|
|
73
|
+
get_cpu_details() -> Dict:
|
|
74
|
+
Fetches the CPU power details by reading the values from the logged CSV file.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
|
|
48
78
|
_osx_exec = "PowerLog"
|
|
49
79
|
_osx_exec_backup = "/Applications/Intel Power Gadget/PowerLog"
|
|
50
80
|
_windows_exec = "PowerLog3.0.exe"
|
|
@@ -60,6 +90,7 @@ class IntelPowerGadget:
|
|
|
60
90
|
self._system = sys.platform.lower()
|
|
61
91
|
self._duration = duration
|
|
62
92
|
self._resolution = resolution
|
|
93
|
+
self._windows_exec_backup = None
|
|
63
94
|
self._setup_cli()
|
|
64
95
|
|
|
65
96
|
def _setup_cli(self) -> None:
|
|
@@ -142,16 +173,17 @@ class IntelPowerGadget:
|
|
|
142
173
|
if returncode != 0:
|
|
143
174
|
logger.warning(
|
|
144
175
|
"Returncode while logging power values using "
|
|
145
|
-
+
|
|
176
|
+
+ "Intel Power Gadget: %s",
|
|
177
|
+
returncode,
|
|
146
178
|
)
|
|
147
179
|
|
|
148
|
-
def get_cpu_details(self
|
|
180
|
+
def get_cpu_details(self) -> Dict:
|
|
149
181
|
"""
|
|
150
182
|
Fetches the CPU Power Details by fetching values from a logged csv file
|
|
151
183
|
in _log_values function
|
|
152
184
|
"""
|
|
153
185
|
self._log_values()
|
|
154
|
-
cpu_details =
|
|
186
|
+
cpu_details = {}
|
|
155
187
|
try:
|
|
156
188
|
cpu_data = pd.read_csv(self._log_file_path).dropna()
|
|
157
189
|
for col_name in cpu_data.columns:
|
|
@@ -164,23 +196,53 @@ class IntelPowerGadget:
|
|
|
164
196
|
except Exception as e:
|
|
165
197
|
logger.info(
|
|
166
198
|
f"Unable to read Intel Power Gadget logged file at {self._log_file_path}\n \
|
|
167
|
-
Exception occurred
|
|
199
|
+
Exception occurred %s",
|
|
200
|
+
e,
|
|
168
201
|
exc_info=True,
|
|
169
202
|
)
|
|
170
203
|
return cpu_details
|
|
171
204
|
|
|
172
|
-
def start(self):
|
|
205
|
+
def start(self) -> None:
|
|
206
|
+
"""
|
|
207
|
+
Placeholder method for starting the Intel Power Gadget monitoring.
|
|
208
|
+
"""
|
|
173
209
|
# TODO: Read energy
|
|
174
|
-
pass
|
|
175
210
|
|
|
176
211
|
|
|
177
212
|
class IntelRAPL:
|
|
213
|
+
"""
|
|
214
|
+
A class to interface Intel's Running Average Power Limit (RAPL) for monitoring CPU power consumption.
|
|
215
|
+
|
|
216
|
+
This class provides methods to set up and read energy consumption data from Intel RAPL files,
|
|
217
|
+
which are available on Linux systems.
|
|
218
|
+
It enables the measurement of CPU energy usage over time and provides methods to fetch
|
|
219
|
+
both dynamic and static CPU energy details.
|
|
220
|
+
|
|
221
|
+
Attributes:
|
|
222
|
+
_lin_rapl_dir (str): The directory path where Intel RAPL files are located.
|
|
223
|
+
_system (str): The platform of the running system, typically used to ensure compatibility.
|
|
224
|
+
_rapl_files (List[RAPLFile]): A list of RAPLFile objects representing the files to read energy data from.
|
|
225
|
+
_cpu_details (Dict): A dictionary storing the latest CPU energy details.
|
|
226
|
+
_last_mesure (int): Placeholder for storing the last measurement time.
|
|
227
|
+
|
|
228
|
+
Methods:
|
|
229
|
+
start():
|
|
230
|
+
Starts monitoring CPU energy consumption.
|
|
231
|
+
|
|
232
|
+
get_cpu_details(duration: Time) -> Dict:
|
|
233
|
+
Fetches the CPU energy deltas over a specified duration by reading values from RAPL files.
|
|
234
|
+
|
|
235
|
+
get_static_cpu_details() -> Dict:
|
|
236
|
+
Returns the CPU details without recalculating them.
|
|
237
|
+
|
|
238
|
+
"""
|
|
239
|
+
|
|
178
240
|
def __init__(self, rapl_dir="/sys/class/powercap/intel-rapl"):
|
|
179
241
|
self._lin_rapl_dir = rapl_dir
|
|
180
242
|
self._system = sys.platform.lower()
|
|
181
|
-
self._rapl_files =
|
|
243
|
+
self._rapl_files = []
|
|
182
244
|
self._setup_rapl()
|
|
183
|
-
self._cpu_details: Dict =
|
|
245
|
+
self._cpu_details: Dict = {}
|
|
184
246
|
|
|
185
247
|
self._last_mesure = 0
|
|
186
248
|
|
|
@@ -199,7 +261,7 @@ class IntelRAPL:
|
|
|
199
261
|
else:
|
|
200
262
|
raise SystemError("Platform not supported by Intel RAPL Interface")
|
|
201
263
|
|
|
202
|
-
def _fetch_rapl_files(self):
|
|
264
|
+
def _fetch_rapl_files(self) -> None:
|
|
203
265
|
"""
|
|
204
266
|
Fetches RAPL files from the RAPL directory
|
|
205
267
|
"""
|
|
@@ -229,19 +291,20 @@ class IntelRAPL:
|
|
|
229
291
|
self._rapl_files.append(
|
|
230
292
|
RAPLFile(name=name, path=rapl_file, max_path=rapl_file_max)
|
|
231
293
|
)
|
|
232
|
-
logger.debug(
|
|
294
|
+
logger.debug("We will read Intel RAPL files at %s", rapl_file)
|
|
233
295
|
except PermissionError as e:
|
|
234
296
|
raise PermissionError(
|
|
235
297
|
"Unable to read Intel RAPL files for CPU power, we will use a constant for your CPU power."
|
|
236
298
|
+ " Please view https://github.com/mlco2/codecarbon/issues/244"
|
|
237
|
-
+
|
|
238
|
-
|
|
299
|
+
+ " for workarounds : %s",
|
|
300
|
+
e,
|
|
301
|
+
) from e
|
|
239
302
|
|
|
240
|
-
def get_cpu_details(self, duration: Time
|
|
303
|
+
def get_cpu_details(self, duration: Time) -> Dict:
|
|
241
304
|
"""
|
|
242
305
|
Fetches the CPU Energy Deltas by fetching values from RAPL files
|
|
243
306
|
"""
|
|
244
|
-
cpu_details =
|
|
307
|
+
cpu_details = {}
|
|
245
308
|
try:
|
|
246
309
|
list(map(lambda rapl_file: rapl_file.delta(duration), self._rapl_files))
|
|
247
310
|
|
|
@@ -255,28 +318,50 @@ class IntelRAPL:
|
|
|
255
318
|
)
|
|
256
319
|
except Exception as e:
|
|
257
320
|
logger.info(
|
|
258
|
-
|
|
259
|
-
Exception occurred
|
|
321
|
+
"Unable to read Intel RAPL files at %s\n \
|
|
322
|
+
Exception occurred %s",
|
|
323
|
+
self._rapl_files,
|
|
324
|
+
e,
|
|
260
325
|
exc_info=True,
|
|
261
326
|
)
|
|
262
|
-
self.
|
|
263
|
-
logger.debug(
|
|
327
|
+
self._cpu_details = cpu_details
|
|
328
|
+
logger.debug("get_cpu_details %s", self._cpu_details)
|
|
264
329
|
return cpu_details
|
|
265
330
|
|
|
266
331
|
def get_static_cpu_details(self) -> Dict:
|
|
267
332
|
"""
|
|
268
333
|
Return CPU details without computing them.
|
|
269
334
|
"""
|
|
270
|
-
logger.debug(
|
|
335
|
+
logger.debug("get_static_cpu_details %s", self._cpu_details)
|
|
271
336
|
|
|
272
|
-
return self.
|
|
337
|
+
return self._cpu_details
|
|
273
338
|
|
|
274
|
-
def start(self):
|
|
339
|
+
def start(self) -> None:
|
|
340
|
+
"""
|
|
341
|
+
Starts monitoring CPU energy consumption.
|
|
342
|
+
"""
|
|
275
343
|
for rapl_file in self._rapl_files:
|
|
276
344
|
rapl_file.start()
|
|
277
345
|
|
|
278
346
|
|
|
279
347
|
class TDP:
|
|
348
|
+
"""
|
|
349
|
+
Represents Thermal Design Power (TDP) for detecting and estimating
|
|
350
|
+
the power consumption of the CPU on a machine.
|
|
351
|
+
|
|
352
|
+
The class provides methods to identify the CPU model, match it with known TDP
|
|
353
|
+
values from a dataset, and return the corresponding power consumption in watts.
|
|
354
|
+
|
|
355
|
+
Attributes:
|
|
356
|
+
model (str): The detected CPU model name.
|
|
357
|
+
tdp (int): The TDP value of the detected CPU in watts.
|
|
358
|
+
|
|
359
|
+
Methods:
|
|
360
|
+
start():
|
|
361
|
+
Placeholder method to initiate TDP analysis.
|
|
362
|
+
|
|
363
|
+
"""
|
|
364
|
+
|
|
280
365
|
def __init__(self):
|
|
281
366
|
self.model, self.tdp = self._main()
|
|
282
367
|
|
|
@@ -323,8 +408,8 @@ class TDP:
|
|
|
323
408
|
THRESHOLD_TOKEN_SET defines the similarity ratio value to consider
|
|
324
409
|
token_set matches (for more detail see fuzz.token_set_ratio).
|
|
325
410
|
"""
|
|
326
|
-
THRESHOLD_DIRECT = 100
|
|
327
|
-
THRESHOLD_TOKEN_SET = 100
|
|
411
|
+
THRESHOLD_DIRECT: int = 100
|
|
412
|
+
THRESHOLD_TOKEN_SET: int = 100
|
|
328
413
|
|
|
329
414
|
direct_match = process.extractOne(
|
|
330
415
|
model_raw,
|
|
@@ -372,12 +457,15 @@ class TDP:
|
|
|
372
457
|
|
|
373
458
|
if power:
|
|
374
459
|
logger.debug(
|
|
375
|
-
|
|
460
|
+
"CPU : We detect a %s with a TDP of %s W",
|
|
461
|
+
cpu_model_detected,
|
|
462
|
+
power,
|
|
376
463
|
)
|
|
377
464
|
return cpu_model_detected, power
|
|
378
465
|
logger.warning(
|
|
379
|
-
|
|
380
|
-
+ " Please contact us."
|
|
466
|
+
"We saw that you have a %s but we don't know it."
|
|
467
|
+
+ " Please contact us.",
|
|
468
|
+
cpu_model_detected,
|
|
381
469
|
)
|
|
382
470
|
return cpu_model_detected, None
|
|
383
471
|
logger.warning(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any, Dict, List, Union
|
|
2
3
|
|
|
3
4
|
import pynvml
|
|
4
5
|
|
|
@@ -8,6 +9,22 @@ from codecarbon.external.logger import logger
|
|
|
8
9
|
|
|
9
10
|
@dataclass
|
|
10
11
|
class GPUDevice:
|
|
12
|
+
"""
|
|
13
|
+
Represents a GPU device with associated energy and power metrics.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
handle (any): An identifier for the GPU device.
|
|
17
|
+
gpu_index (int): The index of the GPU device in the system.
|
|
18
|
+
energy_delta (Energy): The amount of energy consumed by the GPU device
|
|
19
|
+
since the last measurement, expressed in kilowatt-hours (kWh).
|
|
20
|
+
Defaults to an initial value of 0 kWh.
|
|
21
|
+
power (Power): The current power consumption of the GPU device,
|
|
22
|
+
measured in watts (W). Defaults to an initial value of 0 W.
|
|
23
|
+
last_energy (Energy): The last recorded energy reading for the GPU
|
|
24
|
+
device, expressed in kilowatt-hours (kWh). This is used to
|
|
25
|
+
calculate `energy_delta`. Defaults to an initial value of 0 kWh.
|
|
26
|
+
"""
|
|
27
|
+
|
|
11
28
|
handle: any
|
|
12
29
|
gpu_index: int
|
|
13
30
|
# Energy consumed in kWh
|
|
@@ -17,14 +34,14 @@ class GPUDevice:
|
|
|
17
34
|
# Last energy reading in kWh
|
|
18
35
|
last_energy: Energy = field(default_factory=lambda: Energy(0))
|
|
19
36
|
|
|
20
|
-
def start(self):
|
|
37
|
+
def start(self) -> None:
|
|
21
38
|
self.last_energy = self._get_energy_kwh()
|
|
22
39
|
|
|
23
|
-
def __post_init__(self):
|
|
40
|
+
def __post_init__(self) -> None:
|
|
24
41
|
self.last_energy = self._get_energy_kwh()
|
|
25
42
|
self._init_static_details()
|
|
26
43
|
|
|
27
|
-
def _get_energy_kwh(self):
|
|
44
|
+
def _get_energy_kwh(self) -> Energy:
|
|
28
45
|
total_energy_consumption = self._get_total_energy_consumption()
|
|
29
46
|
if total_energy_consumption is None:
|
|
30
47
|
return self.last_energy
|
|
@@ -47,7 +64,7 @@ class GPUDevice:
|
|
|
47
64
|
"power_usage": self.power,
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
def get_static_details(self):
|
|
67
|
+
def get_static_details(self) -> Dict[str, Any]:
|
|
51
68
|
return {
|
|
52
69
|
"name": self._gpu_name,
|
|
53
70
|
"uuid": self._uuid,
|
|
@@ -56,7 +73,7 @@ class GPUDevice:
|
|
|
56
73
|
"gpu_index": self.gpu_index,
|
|
57
74
|
}
|
|
58
75
|
|
|
59
|
-
def _init_static_details(self):
|
|
76
|
+
def _init_static_details(self) -> None:
|
|
60
77
|
self._gpu_name = self._get_gpu_name()
|
|
61
78
|
self._uuid = self._get_uuid()
|
|
62
79
|
self._power_limit = self._get_power_limit()
|
|
@@ -64,7 +81,7 @@ class GPUDevice:
|
|
|
64
81
|
memory = self._get_memory_info()
|
|
65
82
|
self._total_memory = memory.total
|
|
66
83
|
|
|
67
|
-
def get_gpu_details(self):
|
|
84
|
+
def get_gpu_details(self) -> Dict[str, Any]:
|
|
68
85
|
# Memory
|
|
69
86
|
memory = self._get_memory_info()
|
|
70
87
|
|
|
@@ -85,13 +102,13 @@ class GPUDevice:
|
|
|
85
102
|
}
|
|
86
103
|
return device_details
|
|
87
104
|
|
|
88
|
-
def _to_utf8(self, str_or_bytes):
|
|
105
|
+
def _to_utf8(self, str_or_bytes) -> Any:
|
|
89
106
|
if hasattr(str_or_bytes, "decode"):
|
|
90
107
|
return str_or_bytes.decode("utf-8", errors="replace")
|
|
91
108
|
|
|
92
109
|
return str_or_bytes
|
|
93
110
|
|
|
94
|
-
def _get_total_energy_consumption(self):
|
|
111
|
+
def _get_total_energy_consumption(self) -> int:
|
|
95
112
|
"""Returns total energy consumption for this GPU in millijoules (mJ) since the driver was last reloaded
|
|
96
113
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g732ab899b5bd18ac4bfb93c02de4900a
|
|
97
114
|
"""
|
|
@@ -103,14 +120,14 @@ class GPUDevice:
|
|
|
103
120
|
)
|
|
104
121
|
return None
|
|
105
122
|
|
|
106
|
-
def _get_gpu_name(self):
|
|
123
|
+
def _get_gpu_name(self) -> Any:
|
|
107
124
|
"""Returns the name of the GPU device
|
|
108
125
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1ga5361803e044c6fdf3b08523fb6d1481
|
|
109
126
|
"""
|
|
110
127
|
name = pynvml.nvmlDeviceGetName(self.handle)
|
|
111
128
|
return self._to_utf8(name)
|
|
112
129
|
|
|
113
|
-
def _get_uuid(self):
|
|
130
|
+
def _get_uuid(self) -> Any:
|
|
114
131
|
"""Returns the globally unique GPU device UUID
|
|
115
132
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g72710fb20f30f0c2725ce31579832654
|
|
116
133
|
"""
|
|
@@ -123,19 +140,19 @@ class GPUDevice:
|
|
|
123
140
|
"""
|
|
124
141
|
return pynvml.nvmlDeviceGetMemoryInfo(self.handle)
|
|
125
142
|
|
|
126
|
-
def _get_temperature(self):
|
|
143
|
+
def _get_temperature(self) -> int:
|
|
127
144
|
"""Returns degrees in the Celsius scale
|
|
128
145
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g92d1c5182a14dd4be7090e3c1480b121
|
|
129
146
|
"""
|
|
130
147
|
return pynvml.nvmlDeviceGetTemperature(self.handle, pynvml.NVML_TEMPERATURE_GPU)
|
|
131
148
|
|
|
132
|
-
def _get_power_usage(self):
|
|
149
|
+
def _get_power_usage(self) -> int:
|
|
133
150
|
"""Returns power usage in milliwatts
|
|
134
151
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g7ef7dff0ff14238d08a19ad7fb23fc87
|
|
135
152
|
"""
|
|
136
153
|
return pynvml.nvmlDeviceGetPowerUsage(self.handle)
|
|
137
154
|
|
|
138
|
-
def _get_power_limit(self):
|
|
155
|
+
def _get_power_limit(self) -> Union[int, None]:
|
|
139
156
|
"""Returns max power usage in milliwatts
|
|
140
157
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g263b5bf552d5ec7fcd29a088264d10ad
|
|
141
158
|
"""
|
|
@@ -150,13 +167,13 @@ class GPUDevice:
|
|
|
150
167
|
"""
|
|
151
168
|
return pynvml.nvmlDeviceGetUtilizationRates(self.handle).gpu
|
|
152
169
|
|
|
153
|
-
def _get_compute_mode(self):
|
|
170
|
+
def _get_compute_mode(self) -> int:
|
|
154
171
|
"""Returns the compute mode of the GPU
|
|
155
172
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceEnumvs.html#group__nvmlDeviceEnumvs_1gbed1b88f2e3ba39070d31d1db4340233
|
|
156
173
|
"""
|
|
157
174
|
return pynvml.nvmlDeviceGetComputeMode(self.handle)
|
|
158
175
|
|
|
159
|
-
def _get_compute_processes(self):
|
|
176
|
+
def _get_compute_processes(self) -> List:
|
|
160
177
|
"""Returns the list of processes ids having a compute context on the
|
|
161
178
|
device with the memory used
|
|
162
179
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g46ceaea624d5c96e098e03c453419d68
|
|
@@ -168,7 +185,7 @@ class GPUDevice:
|
|
|
168
185
|
except pynvml.NVMLError:
|
|
169
186
|
return []
|
|
170
187
|
|
|
171
|
-
def _get_graphics_processes(self):
|
|
188
|
+
def _get_graphics_processes(self) -> List:
|
|
172
189
|
"""Returns the list of processes ids having a graphics context on the
|
|
173
190
|
device with the memory used
|
|
174
191
|
https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g7eacf7fa7ba4f4485d166736bf31195e
|
|
@@ -182,7 +199,7 @@ class GPUDevice:
|
|
|
182
199
|
|
|
183
200
|
|
|
184
201
|
class AllGPUDevices:
|
|
185
|
-
def __init__(self):
|
|
202
|
+
def __init__(self) -> None:
|
|
186
203
|
if is_gpu_details_available():
|
|
187
204
|
logger.debug("GPU available. Starting setup")
|
|
188
205
|
self.device_count = pynvml.nvmlDeviceGetCount()
|
|
@@ -195,7 +212,7 @@ class AllGPUDevices:
|
|
|
195
212
|
gpu_device = GPUDevice(handle=handle, gpu_index=i)
|
|
196
213
|
self.devices.append(gpu_device)
|
|
197
214
|
|
|
198
|
-
def get_gpu_static_info(self):
|
|
215
|
+
def get_gpu_static_info(self) -> List:
|
|
199
216
|
"""Get all GPUs static information.
|
|
200
217
|
>>> get_gpu_static_info()
|
|
201
218
|
[
|
|
@@ -219,7 +236,7 @@ class AllGPUDevices:
|
|
|
219
236
|
logger.warning("Failed to retrieve gpu static info", exc_info=True)
|
|
220
237
|
return []
|
|
221
238
|
|
|
222
|
-
def get_gpu_details(self):
|
|
239
|
+
def get_gpu_details(self) -> List:
|
|
223
240
|
"""Get all GPUs instantaneous metrics
|
|
224
241
|
>>> get_gpu_details()
|
|
225
242
|
[
|
|
@@ -251,7 +268,7 @@ class AllGPUDevices:
|
|
|
251
268
|
logger.warning("Failed to retrieve gpu information", exc_info=True)
|
|
252
269
|
return []
|
|
253
270
|
|
|
254
|
-
def get_delta(self, last_duration: Time):
|
|
271
|
+
def get_delta(self, last_duration: Time) -> List:
|
|
255
272
|
"""Get difference since last time this function was called
|
|
256
273
|
>>> get_delta()
|
|
257
274
|
[
|
|
@@ -275,7 +292,7 @@ class AllGPUDevices:
|
|
|
275
292
|
return []
|
|
276
293
|
|
|
277
294
|
|
|
278
|
-
def is_gpu_details_available():
|
|
295
|
+
def is_gpu_details_available() -> bool:
|
|
279
296
|
"""Returns True if the GPU details are available."""
|
|
280
297
|
try:
|
|
281
298
|
pynvml.nvmlInit()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from time import
|
|
1
|
+
from time import perf_counter
|
|
2
2
|
|
|
3
3
|
from codecarbon.external.hardware import CPU, GPU, RAM, AppleSiliconChip
|
|
4
4
|
from codecarbon.external.logger import logger
|
|
@@ -25,7 +25,7 @@ class MeasurePowerEnergy:
|
|
|
25
25
|
:param hardware: list of hardware components to measure
|
|
26
26
|
:param pue: Power Usage Effectiveness of the datacenter
|
|
27
27
|
"""
|
|
28
|
-
self._last_measured_time =
|
|
28
|
+
self._last_measured_time = perf_counter()
|
|
29
29
|
self._hardware = hardware
|
|
30
30
|
self._pue = pue
|
|
31
31
|
# TODO: Read initial energy values from hardware
|
|
@@ -40,9 +40,9 @@ class MeasurePowerEnergy:
|
|
|
40
40
|
|
|
41
41
|
def do_measure(self) -> None:
|
|
42
42
|
for hardware in self._hardware:
|
|
43
|
-
h_time =
|
|
43
|
+
h_time = perf_counter()
|
|
44
44
|
# Compute last_duration again for more accuracy
|
|
45
|
-
last_duration =
|
|
45
|
+
last_duration = perf_counter() - self._last_measured_time
|
|
46
46
|
power, energy = hardware.measure_power_and_energy(
|
|
47
47
|
last_duration=last_duration
|
|
48
48
|
)
|
|
@@ -87,7 +87,7 @@ class MeasurePowerEnergy:
|
|
|
87
87
|
)
|
|
88
88
|
else:
|
|
89
89
|
logger.error(f"Unknown hardware type: {hardware} ({type(hardware)})")
|
|
90
|
-
h_time =
|
|
90
|
+
h_time = perf_counter() - h_time
|
|
91
91
|
logger.debug(
|
|
92
92
|
f"{hardware.__class__.__name__} : {hardware.total_power().W:,.2f} "
|
|
93
93
|
+ f"W during {last_duration:,.2f} s [measurement time: {h_time:,.4f}]"
|