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.
Files changed (70) hide show
  1. {codecarbon-2.6.0 → codecarbon-2.7.1}/PKG-INFO +1 -1
  2. codecarbon-2.7.1/codecarbon/_version.py +1 -0
  3. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/api_client.py +0 -3
  4. codecarbon-2.7.1/codecarbon/core/co2_signal.py +62 -0
  5. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/cpu.py +117 -29
  6. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/gpu.py +38 -21
  7. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/measure.py +5 -5
  8. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/powermetrics.py +41 -22
  9. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/rapl.py +0 -3
  10. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/util.py +16 -0
  11. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/hardware/cpu_power.csv +1 -0
  12. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/emissions_tracker.py +137 -42
  13. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/task.py +1 -1
  14. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/input.py +0 -1
  15. codecarbon-2.7.1/codecarbon/lock.py +46 -0
  16. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/emissions_data.py +2 -1
  17. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/file.py +2 -0
  18. {codecarbon-2.6.0 → codecarbon-2.7.1}/pyproject.toml +16 -3
  19. codecarbon-2.6.0/codecarbon/_version.py +0 -1
  20. codecarbon-2.6.0/codecarbon/core/co2_signal.py +0 -35
  21. {codecarbon-2.6.0 → codecarbon-2.7.1}/.gitignore +0 -0
  22. {codecarbon-2.6.0 → codecarbon-2.7.1}/LICENSE +0 -0
  23. {codecarbon-2.6.0 → codecarbon-2.7.1}/README.md +0 -0
  24. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/__init__.py +0 -0
  25. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/cli/__init__.py +0 -0
  26. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/cli/cli_utils.py +0 -0
  27. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/cli/main.py +0 -0
  28. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/__init__.py +0 -0
  29. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/cloud.py +0 -0
  30. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/config.py +0 -0
  31. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/emissions.py +0 -0
  32. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/schemas.py +0 -0
  33. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/core/units.py +0 -0
  34. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/canada_provinces.geojson +0 -0
  35. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/cloud/impact.csv +0 -0
  36. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2016/canada_energy_mix.json +0 -0
  37. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2016/global_energy_mix-old.json +0 -0
  38. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
  39. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/01_get_world_carbon_intensity.ipynb +0 -0
  40. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/02_convert_csv_to_json.ipynb +0 -0
  41. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb +0 -0
  42. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2020/eu-carbon-intensity-electricity.csv +0 -0
  43. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/2023-07-07-22-40-48.png +0 -0
  44. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
  45. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
  46. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/our_world_in_data.ipynb +0 -0
  47. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/data/private_infra/world_energy_mix.csv +0 -0
  48. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/__init__.py +0 -0
  49. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/geography.py +0 -0
  50. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/hardware.py +0 -0
  51. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/logger.py +0 -0
  52. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/external/scheduler.py +0 -0
  53. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output.py +0 -0
  54. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/__init__.py +0 -0
  55. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/base_output.py +0 -0
  56. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/http.py +0 -0
  57. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/logger.py +0 -0
  58. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/__init__.py +0 -0
  59. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/logfire.py +0 -0
  60. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
  61. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/output_methods/metrics/prometheus.py +0 -0
  62. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/__init__.py +0 -0
  63. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/__init__.py +0 -0
  64. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/car_icon.png +0 -0
  65. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/house_icon.png +0 -0
  66. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/assets/tv_icon.png +0 -0
  67. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/carbonboard.py +0 -0
  68. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/carbonboard_on_api.py +0 -0
  69. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/components.py +0 -0
  70. {codecarbon-2.6.0 → codecarbon-2.7.1}/codecarbon/viz/data.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: codecarbon
3
- Version: 2.6.0
3
+ Version: 2.7.1
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/
@@ -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
- + f" IntelPowerGadget : {e}",
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
- + f"IntelRAPL : {e}",
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
- + f"Intel Power Gadget: {returncode}"
176
+ + "Intel Power Gadget: %s",
177
+ returncode,
146
178
  )
147
179
 
148
- def get_cpu_details(self, **kwargs) -> Dict:
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 = dict()
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 {e}",
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 = list()
243
+ self._rapl_files = []
182
244
  self._setup_rapl()
183
- self._cpu_details: Dict = 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(f"We will read Intel RAPL files at {rapl_file}")
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
- + f" for workarounds : {e}"
238
- )
299
+ + " for workarounds : %s",
300
+ e,
301
+ ) from e
239
302
 
240
- def get_cpu_details(self, duration: Time, **kwargs) -> Dict:
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 = dict()
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
- f"Unable to read Intel RAPL files at {self._rapl_files}\n \
259
- Exception occurred {e}",
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.cpu_details = cpu_details
263
- logger.debug(f"get_cpu_details {self.cpu_details}")
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(f"get_static_cpu_details {self.cpu_details}")
335
+ logger.debug("get_static_cpu_details %s", self._cpu_details)
271
336
 
272
- return self.cpu_details
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
- f"CPU : We detect a {cpu_model_detected} with a TDP of {power} W"
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
- f"We saw that you have a {cpu_model_detected} but we don't know it."
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 time
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 = 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 = time.time()
43
+ h_time = perf_counter()
44
44
  # Compute last_duration again for more accuracy
45
- last_duration = time.time() - self._last_measured_time
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 = time.time() - 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}]"