codecarbon 2.3.2__tar.gz → 2.3.4__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.3.2/codecarbon.egg-info → codecarbon-2.3.4}/PKG-INFO +2 -2
- codecarbon-2.3.4/codecarbon/_version.py +1 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/cpu.py +47 -49
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/emissions.py +29 -12
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/measure.py +18 -3
- codecarbon-2.3.4/codecarbon/core/powermetrics.py +173 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/emissions_tracker.py +41 -8
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/external/hardware.py +74 -1
- {codecarbon-2.3.2 → codecarbon-2.3.4/codecarbon.egg-info}/PKG-INFO +2 -2
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon.egg-info/SOURCES.txt +2 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon.egg-info/requires.txt +1 -1
- {codecarbon-2.3.2 → codecarbon-2.3.4}/setup.py +2 -2
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_cpu.py +20 -14
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_emissions_tracker_constant.py +31 -0
- codecarbon-2.3.4/tests/test_powermetrics.py +35 -0
- codecarbon-2.3.2/codecarbon/_version.py +0 -1
- {codecarbon-2.3.2 → codecarbon-2.3.4}/LICENSE +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/README.md +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/dependencies.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/emissions.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/experiments.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/organizations.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/projects.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/runs.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/teams.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/domain/users.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/errors.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/authenticate.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/emissions.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/experiments.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/organizations.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/projects.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/runs.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/teams.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/users.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/schemas.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/config.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/database/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/database/database.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/logger.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/container.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/main.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/setup.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/cli/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/cli/cli_utils.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/cli/main.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/api_client.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/cloud.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/co2_signal.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/config.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/gpu.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/rapl.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/schemas.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/units.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/core/util.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/cloud/impact.csv +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/hardware/cpu_power.csv +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/private_infra/2016/canada_energy_mix.json +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/external/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/external/geography.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/external/logger.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/external/scheduler.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/external/task.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/input.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/output.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/prometheus/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/prometheus/metric_definitions.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/prometheus/prometheus.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/assets/__init__.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/assets/car_icon.png +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/assets/house_icon.png +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/assets/tv_icon.png +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/carbonboard.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/carbonboard_on_api.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/components.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/viz/data.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon.egg-info/dependency_links.txt +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon.egg-info/entry_points.txt +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon.egg-info/top_level.txt +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/setup.cfg +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_api_call.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_cloud.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_co2_signal.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_config.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_core_util.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_emissions.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_emissions_tracker.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_emissions_tracker_flush.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_energy.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_geography.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_gpu.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_logging_output.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_ram.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_tracking_inference.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/test_viz_data.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/testdata.py +0 -0
- {codecarbon-2.3.2 → codecarbon-2.3.4}/tests/testutils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codecarbon
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.4
|
|
4
4
|
Author: Mila, DataForGood, BCG GAMMA, Comet.ml, Haverford College
|
|
5
5
|
Classifier: Natural Language :: English
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.7
|
|
@@ -18,7 +18,7 @@ Requires-Dist: pynvml
|
|
|
18
18
|
Requires-Dist: requests
|
|
19
19
|
Requires-Dist: psutil
|
|
20
20
|
Requires-Dist: py-cpuinfo
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: rapidfuzz
|
|
22
22
|
Requires-Dist: click
|
|
23
23
|
Requires-Dist: prometheus_client
|
|
24
24
|
Provides-Extra: viz
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.4"
|
|
@@ -7,13 +7,10 @@ import os
|
|
|
7
7
|
import shutil
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
|
-
import warnings
|
|
11
10
|
from typing import Dict, Tuple
|
|
12
11
|
|
|
13
12
|
import pandas as pd
|
|
14
|
-
|
|
15
|
-
with warnings.catch_warnings(record=True) as w:
|
|
16
|
-
from fuzzywuzzy import fuzz
|
|
13
|
+
from rapidfuzz import fuzz, process, utils
|
|
17
14
|
|
|
18
15
|
from codecarbon.core.rapl import RAPLFile
|
|
19
16
|
from codecarbon.core.units import Time
|
|
@@ -50,8 +47,6 @@ class IntelPowerGadget:
|
|
|
50
47
|
_osx_exec = "PowerLog"
|
|
51
48
|
_osx_exec_backup = "/Applications/Intel Power Gadget/PowerLog"
|
|
52
49
|
_windows_exec = "PowerLog3.0.exe"
|
|
53
|
-
# TODO: There is now a 3.6 version.
|
|
54
|
-
_windows_exec_backup = "C:\\Program Files\\Intel\\Power Gadget 3.5\\PowerLog3.0.exe"
|
|
55
50
|
|
|
56
51
|
def __init__(
|
|
57
52
|
self,
|
|
@@ -71,6 +66,7 @@ class IntelPowerGadget:
|
|
|
71
66
|
Setup cli command to run Intel Power Gadget
|
|
72
67
|
"""
|
|
73
68
|
if self._system.startswith("win"):
|
|
69
|
+
self._get_windows_exec_backup()
|
|
74
70
|
if shutil.which(self._windows_exec):
|
|
75
71
|
self._cli = shutil.which(
|
|
76
72
|
self._windows_exec
|
|
@@ -93,6 +89,27 @@ class IntelPowerGadget:
|
|
|
93
89
|
else:
|
|
94
90
|
raise SystemError("Platform not supported by Intel Power Gadget")
|
|
95
91
|
|
|
92
|
+
def _get_windows_exec_backup(self) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Find the windows executable for the current version of intel power gadget.
|
|
95
|
+
Example: "C:\\Program Files\\Intel\\Power Gadget 3.5\\PowerLog3.0.exe"
|
|
96
|
+
"""
|
|
97
|
+
parent_folder = "C:\\Program Files\\Intel\\"
|
|
98
|
+
|
|
99
|
+
# Get a list of all subdirectories in the parent folder
|
|
100
|
+
subfolders = [f.name for f in os.scandir(parent_folder) if f.is_dir()]
|
|
101
|
+
|
|
102
|
+
# Look for a folder that contains "Power Gadget" in its name
|
|
103
|
+
desired_folder = next(
|
|
104
|
+
(folder for folder in subfolders if "Power Gadget" in folder), None
|
|
105
|
+
)
|
|
106
|
+
if desired_folder:
|
|
107
|
+
self._windows_exec_backup = os.path.join(
|
|
108
|
+
parent_folder, desired_folder, self._windows_exec
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
self._windows_exec_backup = None
|
|
112
|
+
|
|
96
113
|
def _log_values(self):
|
|
97
114
|
"""
|
|
98
115
|
Logs output from Intel Power Gadget command line to a file
|
|
@@ -278,27 +295,6 @@ class TDP:
|
|
|
278
295
|
return power
|
|
279
296
|
return None
|
|
280
297
|
|
|
281
|
-
@staticmethod
|
|
282
|
-
def _get_cpus(cpu_df, cpu_idxs) -> list:
|
|
283
|
-
return [cpu_df["Name"][idx] for idx in cpu_idxs]
|
|
284
|
-
|
|
285
|
-
@staticmethod
|
|
286
|
-
def _get_direct_matches(moodel: str, cpu_df: pd.DataFrame) -> list:
|
|
287
|
-
model_l = moodel.lower()
|
|
288
|
-
return [fuzz.ratio(model_l, cpu.lower()) for cpu in cpu_df["Name"]]
|
|
289
|
-
|
|
290
|
-
@staticmethod
|
|
291
|
-
def _get_token_set_matches(model: str, cpu_df: pd.DataFrame) -> list:
|
|
292
|
-
return [fuzz.token_set_ratio(model, cpu) for cpu in cpu_df["Name"]]
|
|
293
|
-
|
|
294
|
-
@staticmethod
|
|
295
|
-
def _get_single_direct_match(
|
|
296
|
-
ratios: list, max_ratio: int, cpu_df: pd.DataFrame
|
|
297
|
-
) -> str:
|
|
298
|
-
idx = ratios.index(max_ratio)
|
|
299
|
-
cpu_matched = cpu_df["Name"].iloc[idx]
|
|
300
|
-
return cpu_matched
|
|
301
|
-
|
|
302
298
|
def _get_matching_cpu(
|
|
303
299
|
self, model_raw: str, cpu_df: pd.DataFrame, greedy=False
|
|
304
300
|
) -> str:
|
|
@@ -332,33 +328,35 @@ class TDP:
|
|
|
332
328
|
THRESHOLD_DIRECT = 100
|
|
333
329
|
THRESHOLD_TOKEN_SET = 100
|
|
334
330
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
331
|
+
direct_match = process.extractOne(
|
|
332
|
+
model_raw,
|
|
333
|
+
cpu_df["Name"],
|
|
334
|
+
processor=lambda s: s.lower(),
|
|
335
|
+
scorer=fuzz.ratio,
|
|
336
|
+
score_cutoff=THRESHOLD_DIRECT,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
if direct_match:
|
|
340
|
+
return direct_match[0]
|
|
339
341
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
indirect_matches = process.extract(
|
|
343
|
+
model_raw,
|
|
344
|
+
cpu_df["Name"],
|
|
345
|
+
processor=utils.default_process,
|
|
346
|
+
scorer=fuzz.token_set_ratio,
|
|
347
|
+
score_cutoff=THRESHOLD_TOKEN_SET,
|
|
348
|
+
)
|
|
346
349
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
350
|
+
if indirect_matches:
|
|
351
|
+
if (
|
|
352
|
+
greedy
|
|
353
|
+
or len(indirect_matches) == 1
|
|
354
|
+
or indirect_matches[0][1] != indirect_matches[1][1]
|
|
355
|
+
):
|
|
356
|
+
return indirect_matches[0][0]
|
|
352
357
|
|
|
353
|
-
if (cpu_machings and len(cpu_machings) == 1) or greedy:
|
|
354
|
-
cpu_matched = cpu_machings[0]
|
|
355
|
-
return cpu_matched
|
|
356
358
|
return None
|
|
357
359
|
|
|
358
|
-
@staticmethod
|
|
359
|
-
def _get_max_idxs(ratios: list, max_ratio: int) -> list:
|
|
360
|
-
return [idx for idx, ratio in enumerate(ratios) if ratio == max_ratio]
|
|
361
|
-
|
|
362
360
|
def _main(self) -> Tuple[str, int]:
|
|
363
361
|
"""
|
|
364
362
|
Get CPU power from constant mode
|
|
@@ -71,32 +71,49 @@ class Emissions:
|
|
|
71
71
|
Returns the Country Name where the cloud region is located
|
|
72
72
|
"""
|
|
73
73
|
df: pd.DataFrame = self._data_source.get_cloud_emissions_data()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
flags = (df["provider"] == cloud.provider) & (df["region"] == cloud.region)
|
|
75
|
+
selected = df.loc[flags]
|
|
76
|
+
if not len(selected):
|
|
77
|
+
raise ValueError(
|
|
78
|
+
"Unable to find country name for "
|
|
79
|
+
f"cloud_provider={cloud.provider}, "
|
|
80
|
+
f"cloud_region={cloud.region}"
|
|
81
|
+
)
|
|
82
|
+
return selected["country_name"].item()
|
|
77
83
|
|
|
78
84
|
def get_cloud_country_iso_code(self, cloud: CloudMetadata) -> str:
|
|
79
85
|
"""
|
|
80
86
|
Returns the Country ISO Code where the cloud region is located
|
|
81
87
|
"""
|
|
82
88
|
df: pd.DataFrame = self._data_source.get_cloud_emissions_data()
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
flags = (df["provider"] == cloud.provider) & (df["region"] == cloud.region)
|
|
90
|
+
selected = df.loc[flags]
|
|
91
|
+
if not len(selected):
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"Unable to find country name for "
|
|
94
|
+
f"cloud_provider={cloud.provider}, "
|
|
95
|
+
f"cloud_region={cloud.region}"
|
|
96
|
+
)
|
|
97
|
+
return selected["countryIsoCode"].item()
|
|
86
98
|
|
|
87
99
|
def get_cloud_geo_region(self, cloud: CloudMetadata) -> str:
|
|
88
100
|
"""
|
|
89
101
|
Returns the State/City where the cloud region is located
|
|
90
102
|
"""
|
|
91
103
|
df: pd.DataFrame = self._data_source.get_cloud_emissions_data()
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
flags = (df["provider"] == cloud.provider) & (df["region"] == cloud.region)
|
|
105
|
+
selected = df.loc[flags]
|
|
106
|
+
if not len(selected):
|
|
107
|
+
raise ValueError(
|
|
108
|
+
"Unable to find country name for "
|
|
109
|
+
f"cloud_provider={cloud.provider}, "
|
|
110
|
+
f"cloud_region={cloud.region}"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
state = selected["state"].item()
|
|
95
114
|
if state is not None:
|
|
96
115
|
return state
|
|
97
|
-
city =
|
|
98
|
-
(df["provider"] == cloud.provider) & (df["region"] == cloud.region)
|
|
99
|
-
]["city"].item()
|
|
116
|
+
city = selected["city"].item()
|
|
100
117
|
return city
|
|
101
118
|
|
|
102
119
|
def get_private_infra_emissions(self, energy: Energy, geo: GeoMetadata) -> float:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from time import time
|
|
2
2
|
|
|
3
|
-
from codecarbon.external.hardware import CPU, GPU, RAM
|
|
3
|
+
from codecarbon.external.hardware import CPU, GPU, RAM, AppleSiliconChip
|
|
4
4
|
from codecarbon.external.logger import logger
|
|
5
5
|
|
|
6
6
|
|
|
@@ -67,9 +67,24 @@ class MeasurePowerEnergy:
|
|
|
67
67
|
self._total_ram_energy += energy
|
|
68
68
|
self._ram_power = power
|
|
69
69
|
logger.info(
|
|
70
|
-
f"Energy consumed for RAM : {self._total_ram_energy.kWh:.6f} kWh"
|
|
71
|
-
+ f"
|
|
70
|
+
f"Energy consumed for RAM : {self._total_ram_energy.kWh:.6f} kWh."
|
|
71
|
+
+ f"RAM Power : {self._ram_power.W} W"
|
|
72
72
|
)
|
|
73
|
+
elif isinstance(hardware, AppleSiliconChip):
|
|
74
|
+
if hardware.chip_part == "CPU":
|
|
75
|
+
self._total_cpu_energy += energy
|
|
76
|
+
self._cpu_power = power
|
|
77
|
+
logger.info(
|
|
78
|
+
f"Energy consumed for AppleSilicon CPU : {self._total_cpu_energy.kWh:.6f} kWh"
|
|
79
|
+
+ f".Apple Silicon CPU Power : {self._cpu_power.W} W"
|
|
80
|
+
)
|
|
81
|
+
elif hardware.chip_part == "GPU":
|
|
82
|
+
self._total_gpu_energy += energy
|
|
83
|
+
self._gpu_power = power
|
|
84
|
+
logger.info(
|
|
85
|
+
f"Energy consumed for AppleSilicon GPU : {self._total_gpu_energy.kWh:.6f} kWh"
|
|
86
|
+
+ f".Apple Silicon GPU Power : {self._gpu_power.W} W"
|
|
87
|
+
)
|
|
73
88
|
else:
|
|
74
89
|
logger.error(f"Unknown hardware type: {hardware} ({type(hardware)})")
|
|
75
90
|
h_time = time.time() - h_time
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Dict
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from codecarbon.core.util import detect_cpu_model
|
|
11
|
+
from codecarbon.external.logger import logger
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_powermetrics_available():
|
|
15
|
+
try:
|
|
16
|
+
ApplePowermetrics()
|
|
17
|
+
response = _has_powermetrics_sudo()
|
|
18
|
+
return response
|
|
19
|
+
except Exception as e:
|
|
20
|
+
logger.debug(
|
|
21
|
+
"Not using PowerMetrics, an exception occurred while instantiating"
|
|
22
|
+
+ f" Powermetrics : {e}",
|
|
23
|
+
)
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _has_powermetrics_sudo():
|
|
28
|
+
process = subprocess.Popen(
|
|
29
|
+
[
|
|
30
|
+
"sudo",
|
|
31
|
+
"powermetrics",
|
|
32
|
+
"--samplers",
|
|
33
|
+
"cpu_power",
|
|
34
|
+
"-n",
|
|
35
|
+
"1",
|
|
36
|
+
"-i",
|
|
37
|
+
"1",
|
|
38
|
+
"-o",
|
|
39
|
+
"/dev/null",
|
|
40
|
+
],
|
|
41
|
+
stdout=subprocess.PIPE,
|
|
42
|
+
stderr=subprocess.PIPE,
|
|
43
|
+
text=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
_, stderr = process.communicate()
|
|
47
|
+
|
|
48
|
+
if re.search(r"[sudo].*password", stderr):
|
|
49
|
+
logger.debug(
|
|
50
|
+
"""Not using PowerMetrics, sudo password prompt detected.
|
|
51
|
+
If you want to enable Powermetrics please modify your sudoers file
|
|
52
|
+
as described in :
|
|
53
|
+
https://mlco2.github.io/codecarbon/methodology.html#power-usage
|
|
54
|
+
"""
|
|
55
|
+
)
|
|
56
|
+
return False
|
|
57
|
+
if process.returncode != 0:
|
|
58
|
+
raise Exception("Return code != 0")
|
|
59
|
+
else:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ApplePowermetrics:
|
|
64
|
+
_osx_silicon_exec = "powermetrics"
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
output_dir: str = ".",
|
|
69
|
+
n_points=10,
|
|
70
|
+
interval=100,
|
|
71
|
+
log_file_name="powermetrics_log.txt",
|
|
72
|
+
):
|
|
73
|
+
self._log_file_path = os.path.join(output_dir, log_file_name)
|
|
74
|
+
self._system = sys.platform.lower()
|
|
75
|
+
self._n_points = n_points
|
|
76
|
+
self._interval = interval
|
|
77
|
+
self._setup_cli()
|
|
78
|
+
|
|
79
|
+
def _setup_cli(self):
|
|
80
|
+
"""
|
|
81
|
+
Setup cli command to run Powermetrics
|
|
82
|
+
"""
|
|
83
|
+
if self._system.startswith("darwin"):
|
|
84
|
+
cpu_model = detect_cpu_model()
|
|
85
|
+
if cpu_model.startswith("Apple"):
|
|
86
|
+
if shutil.which(self._osx_silicon_exec):
|
|
87
|
+
self._cli = self._osx_silicon_exec
|
|
88
|
+
else:
|
|
89
|
+
raise FileNotFoundError(
|
|
90
|
+
f"Powermetrics executable not found on {self._system}"
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
raise SystemError("Platform not supported by Powermetrics")
|
|
94
|
+
|
|
95
|
+
def _log_values(self):
|
|
96
|
+
"""
|
|
97
|
+
Logs output from Powermetrics to a file
|
|
98
|
+
"""
|
|
99
|
+
returncode = None
|
|
100
|
+
|
|
101
|
+
if self._system.startswith("darwin"):
|
|
102
|
+
# Run the powermetrics command with sudo and capture its output
|
|
103
|
+
cmd = [
|
|
104
|
+
"sudo",
|
|
105
|
+
"powermetrics",
|
|
106
|
+
"-n",
|
|
107
|
+
str(self._n_points),
|
|
108
|
+
"",
|
|
109
|
+
"--samplers",
|
|
110
|
+
"cpu_power",
|
|
111
|
+
"--format",
|
|
112
|
+
"csv",
|
|
113
|
+
"-i",
|
|
114
|
+
str(self._interval),
|
|
115
|
+
"-o",
|
|
116
|
+
self._log_file_path,
|
|
117
|
+
]
|
|
118
|
+
returncode = subprocess.call(cmd, universal_newlines=True)
|
|
119
|
+
|
|
120
|
+
else:
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
if returncode != 0:
|
|
124
|
+
logger.warning(
|
|
125
|
+
"Returncode while logging power values using "
|
|
126
|
+
+ f"Powermetrics: {returncode}"
|
|
127
|
+
)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
def get_details(self, **kwargs) -> Dict:
|
|
131
|
+
"""
|
|
132
|
+
Fetches the CPU Power Details by fetching values from a logged csv file
|
|
133
|
+
in _log_values function
|
|
134
|
+
"""
|
|
135
|
+
self._log_values()
|
|
136
|
+
details = dict()
|
|
137
|
+
try:
|
|
138
|
+
with open(self._log_file_path) as f:
|
|
139
|
+
logfile = f.read()
|
|
140
|
+
cpu_pattern = r"CPU Power: (\d+) mW"
|
|
141
|
+
cpu_power_list = re.findall(cpu_pattern, logfile)
|
|
142
|
+
|
|
143
|
+
details["CPU Power"] = np.mean(
|
|
144
|
+
[float(power) / 1000 for power in cpu_power_list]
|
|
145
|
+
)
|
|
146
|
+
details["CPU Energy Delta"] = np.sum(
|
|
147
|
+
[
|
|
148
|
+
(self._interval / 1000) * (float(power) / 1000)
|
|
149
|
+
for power in cpu_power_list
|
|
150
|
+
]
|
|
151
|
+
)
|
|
152
|
+
gpu_pattern = r"GPU Power: (\d+) mW"
|
|
153
|
+
gpu_power_list = re.findall(gpu_pattern, logfile)
|
|
154
|
+
details["GPU Power"] = np.mean(
|
|
155
|
+
[float(power) / 1000 for power in gpu_power_list]
|
|
156
|
+
)
|
|
157
|
+
details["GPU Energy Delta"] = np.sum(
|
|
158
|
+
[
|
|
159
|
+
(self._interval / 1000) * (float(power) / 1000)
|
|
160
|
+
for power in gpu_power_list
|
|
161
|
+
]
|
|
162
|
+
)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.info(
|
|
165
|
+
f"Unable to read Powermetrics logged file at {self._log_file_path}\n \
|
|
166
|
+
Exception occurred {e}",
|
|
167
|
+
exc_info=True,
|
|
168
|
+
)
|
|
169
|
+
return details
|
|
170
|
+
|
|
171
|
+
def start(self):
|
|
172
|
+
# TODO: Read energy
|
|
173
|
+
pass
|
|
@@ -14,13 +14,13 @@ from functools import wraps
|
|
|
14
14
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
15
15
|
|
|
16
16
|
from codecarbon._version import __version__
|
|
17
|
-
from codecarbon.core import cpu, gpu
|
|
17
|
+
from codecarbon.core import cpu, gpu, powermetrics
|
|
18
18
|
from codecarbon.core.config import get_hierarchical_config, parse_gpu_ids
|
|
19
19
|
from codecarbon.core.emissions import Emissions
|
|
20
20
|
from codecarbon.core.units import Energy, Power, Time
|
|
21
21
|
from codecarbon.core.util import count_cpus, suppress
|
|
22
22
|
from codecarbon.external.geography import CloudMetadata, GeoMetadata
|
|
23
|
-
from codecarbon.external.hardware import CPU, GPU, RAM
|
|
23
|
+
from codecarbon.external.hardware import CPU, GPU, RAM, AppleSiliconChip
|
|
24
24
|
from codecarbon.external.logger import logger, set_logger_format, set_logger_level
|
|
25
25
|
from codecarbon.external.scheduler import PeriodicScheduler
|
|
26
26
|
from codecarbon.external.task import Task
|
|
@@ -275,7 +275,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
275
275
|
logger.info("[setup] RAM Tracking...")
|
|
276
276
|
ram = RAM(tracking_mode=self._tracking_mode)
|
|
277
277
|
self._conf["ram_total_size"] = ram.machine_memory_GB
|
|
278
|
-
self._hardware: List[Union[RAM, CPU, GPU]] = [ram]
|
|
278
|
+
self._hardware: List[Union[RAM, CPU, GPU, AppleSiliconChip]] = [ram]
|
|
279
279
|
|
|
280
280
|
# Hardware detection
|
|
281
281
|
logger.info("[setup] GPU Tracking...")
|
|
@@ -293,7 +293,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
293
293
|
logger.info("No GPU found.")
|
|
294
294
|
|
|
295
295
|
logger.info("[setup] CPU Tracking...")
|
|
296
|
-
if cpu.is_powergadget_available():
|
|
296
|
+
if cpu.is_powergadget_available() and self._default_cpu_power is None:
|
|
297
297
|
logger.info("Tracking Intel CPU via Power Gadget")
|
|
298
298
|
hardware = CPU.from_utils(self._output_dir, "intel_power_gadget")
|
|
299
299
|
self._hardware.append(hardware)
|
|
@@ -303,6 +303,23 @@ class BaseEmissionsTracker(ABC):
|
|
|
303
303
|
hardware = CPU.from_utils(self._output_dir, "intel_rapl")
|
|
304
304
|
self._hardware.append(hardware)
|
|
305
305
|
self._conf["cpu_model"] = hardware.get_model()
|
|
306
|
+
elif (
|
|
307
|
+
powermetrics.is_powermetrics_available() and self._default_cpu_power is None
|
|
308
|
+
):
|
|
309
|
+
logger.info("Tracking Apple CPU and GPU via PowerMetrics")
|
|
310
|
+
hardware_cpu = AppleSiliconChip.from_utils(
|
|
311
|
+
self._output_dir, chip_part="CPU"
|
|
312
|
+
)
|
|
313
|
+
self._hardware.append(hardware_cpu)
|
|
314
|
+
self._conf["cpu_model"] = hardware_cpu.get_model()
|
|
315
|
+
|
|
316
|
+
hardware_gpu = AppleSiliconChip.from_utils(
|
|
317
|
+
self._output_dir, chip_part="GPU"
|
|
318
|
+
)
|
|
319
|
+
self._hardware.append(hardware_gpu)
|
|
320
|
+
|
|
321
|
+
self._conf["gpu_model"] = hardware_gpu.get_model()
|
|
322
|
+
self._conf["gpu_count"] = 1
|
|
306
323
|
else:
|
|
307
324
|
logger.warning(
|
|
308
325
|
"No CPU tracking mode found. Falling back on CPU constant mode."
|
|
@@ -640,9 +657,10 @@ class BaseEmissionsTracker(ABC):
|
|
|
640
657
|
h_time = time.time()
|
|
641
658
|
# Compute last_duration again for more accuracy
|
|
642
659
|
last_duration = time.time() - self._last_measured_time
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
660
|
+
(
|
|
661
|
+
power,
|
|
662
|
+
energy,
|
|
663
|
+
) = hardware.measure_power_and_energy(last_duration=last_duration)
|
|
646
664
|
# Apply the PUE of the datacenter to the consumed energy
|
|
647
665
|
energy *= self._pue
|
|
648
666
|
self._total_energy += energy
|
|
@@ -667,6 +685,21 @@ class BaseEmissionsTracker(ABC):
|
|
|
667
685
|
f"Energy consumed for RAM : {self._total_ram_energy.kWh:.6f} kWh"
|
|
668
686
|
+ f". RAM Power : {self._ram_power.W} W"
|
|
669
687
|
)
|
|
688
|
+
elif isinstance(hardware, AppleSiliconChip):
|
|
689
|
+
if hardware.chip_part == "CPU":
|
|
690
|
+
self._total_cpu_energy += energy
|
|
691
|
+
self._cpu_power = power
|
|
692
|
+
logger.info(
|
|
693
|
+
f"Energy consumed for all CPUs : {self._total_cpu_energy.kWh:.6f} kWh"
|
|
694
|
+
+ f". Total CPU Power : {self._cpu_power.W} W"
|
|
695
|
+
)
|
|
696
|
+
elif hardware.chip_part == "GPU":
|
|
697
|
+
self._total_gpu_energy += energy
|
|
698
|
+
self._gpu_power = power
|
|
699
|
+
logger.info(
|
|
700
|
+
f"Energy consumed for all GPUs : {self._total_gpu_energy.kWh:.6f} kWh"
|
|
701
|
+
+ f". Total GPU Power : {self._gpu_power.W} W"
|
|
702
|
+
)
|
|
670
703
|
else:
|
|
671
704
|
logger.error(f"Unknown hardware type: {hardware} ({type(hardware)})")
|
|
672
705
|
h_time = time.time() - h_time
|
|
@@ -704,7 +737,7 @@ class BaseEmissionsTracker(ABC):
|
|
|
704
737
|
emissions = self._prepare_emissions_data(delta=True)
|
|
705
738
|
logger.info(
|
|
706
739
|
f"{emissions.emissions_rate * 1000:.6f} g.CO2eq/s mean an estimation of "
|
|
707
|
-
+ f"{emissions.emissions_rate*3600*24*365:,} kg.CO2eq/year"
|
|
740
|
+
+ f"{emissions.emissions_rate * 3600 * 24 * 365:,} kg.CO2eq/year"
|
|
708
741
|
)
|
|
709
742
|
if self._cc_api__out:
|
|
710
743
|
self._cc_api__out.out(emissions)
|
|
@@ -11,6 +11,7 @@ import psutil
|
|
|
11
11
|
|
|
12
12
|
from codecarbon.core.cpu import IntelPowerGadget, IntelRAPL
|
|
13
13
|
from codecarbon.core.gpu import AllGPUDevices
|
|
14
|
+
from codecarbon.core.powermetrics import ApplePowermetrics
|
|
14
15
|
from codecarbon.core.units import Energy, Power, Time
|
|
15
16
|
from codecarbon.core.util import SLURM_JOB_ID, detect_cpu_model
|
|
16
17
|
from codecarbon.external.logger import logger
|
|
@@ -202,7 +203,7 @@ class CPU(BaseHardware):
|
|
|
202
203
|
return super().measure_power_and_energy(last_duration=last_duration)
|
|
203
204
|
|
|
204
205
|
def start(self):
|
|
205
|
-
if self._mode in ["intel_power_gadget", "intel_rapl"]:
|
|
206
|
+
if self._mode in ["intel_power_gadget", "intel_rapl", "apple_powermetrics"]:
|
|
206
207
|
self._intel_interface.start()
|
|
207
208
|
pass
|
|
208
209
|
|
|
@@ -395,3 +396,75 @@ class RAM(BaseHardware):
|
|
|
395
396
|
ram_power = Power.from_watts(0)
|
|
396
397
|
|
|
397
398
|
return ram_power
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@dataclass
|
|
402
|
+
class AppleSiliconChip(BaseHardware):
|
|
403
|
+
def __init__(
|
|
404
|
+
self,
|
|
405
|
+
output_dir: str,
|
|
406
|
+
model: str,
|
|
407
|
+
chip_part: str = "CPU",
|
|
408
|
+
):
|
|
409
|
+
self._output_dir = output_dir
|
|
410
|
+
self._model = model
|
|
411
|
+
self._interface = ApplePowermetrics(self._output_dir)
|
|
412
|
+
self.chip_part = chip_part
|
|
413
|
+
|
|
414
|
+
def __repr__(self) -> str:
|
|
415
|
+
return f"AppleSiliconChip ({self._model} > {self.chip_part})"
|
|
416
|
+
|
|
417
|
+
def _get_power(self) -> Power:
|
|
418
|
+
"""
|
|
419
|
+
Get Chip part power
|
|
420
|
+
Args:
|
|
421
|
+
chip_part (str): Chip part to get power from (CPU, GPU)
|
|
422
|
+
:return: power in kW
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
all_details: Dict = self._interface.get_details()
|
|
426
|
+
|
|
427
|
+
power = 0
|
|
428
|
+
for metric, value in all_details.items():
|
|
429
|
+
if re.match(rf"^{self.chip_part} Power", metric):
|
|
430
|
+
power += value
|
|
431
|
+
logger.debug(f"_get_power_from_cpus - MATCH {metric} : {value}")
|
|
432
|
+
|
|
433
|
+
else:
|
|
434
|
+
logger.debug(f"_get_power_from_cpus - DONT MATCH {metric} : {value}")
|
|
435
|
+
return Power.from_watts(power)
|
|
436
|
+
|
|
437
|
+
def _get_energy(self, delay: Time) -> Energy:
|
|
438
|
+
"""
|
|
439
|
+
Get Chip part energy deltas
|
|
440
|
+
Args:
|
|
441
|
+
chip_part (str): Chip part to get power from (Processor, GPU, etc.)
|
|
442
|
+
:return: energy in kWh
|
|
443
|
+
"""
|
|
444
|
+
all_details: Dict = self._interface.get_details(delay)
|
|
445
|
+
|
|
446
|
+
energy = 0
|
|
447
|
+
for metric, value in all_details.items():
|
|
448
|
+
if re.match(rf"^{self.chip_part} Energy Delta_\d", metric):
|
|
449
|
+
energy += value
|
|
450
|
+
return Energy.from_energy(energy)
|
|
451
|
+
|
|
452
|
+
def total_power(self) -> Power:
|
|
453
|
+
return self._get_power()
|
|
454
|
+
|
|
455
|
+
def start(self):
|
|
456
|
+
self._interface.start()
|
|
457
|
+
|
|
458
|
+
def get_model(self):
|
|
459
|
+
return self._model
|
|
460
|
+
|
|
461
|
+
@classmethod
|
|
462
|
+
def from_utils(
|
|
463
|
+
cls, output_dir: str, model: Optional[str] = None, chip_part: str = "Processor"
|
|
464
|
+
) -> "AppleSiliconChip":
|
|
465
|
+
if model is None:
|
|
466
|
+
model = detect_cpu_model()
|
|
467
|
+
if model is None:
|
|
468
|
+
logger.warning("Could not read AppleSiliconChip model.")
|
|
469
|
+
|
|
470
|
+
return cls(output_dir=output_dir, model=model, chip_part=chip_part)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codecarbon
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.4
|
|
4
4
|
Author: Mila, DataForGood, BCG GAMMA, Comet.ml, Haverford College
|
|
5
5
|
Classifier: Natural Language :: English
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.7
|
|
@@ -18,7 +18,7 @@ Requires-Dist: pynvml
|
|
|
18
18
|
Requires-Dist: requests
|
|
19
19
|
Requires-Dist: psutil
|
|
20
20
|
Requires-Dist: py-cpuinfo
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: rapidfuzz
|
|
22
22
|
Requires-Dist: click
|
|
23
23
|
Requires-Dist: prometheus_client
|
|
24
24
|
Provides-Extra: viz
|
|
@@ -54,6 +54,7 @@ codecarbon/core/cpu.py
|
|
|
54
54
|
codecarbon/core/emissions.py
|
|
55
55
|
codecarbon/core/gpu.py
|
|
56
56
|
codecarbon/core/measure.py
|
|
57
|
+
codecarbon/core/powermetrics.py
|
|
57
58
|
codecarbon/core/rapl.py
|
|
58
59
|
codecarbon/core/schemas.py
|
|
59
60
|
codecarbon/core/units.py
|
|
@@ -96,6 +97,7 @@ tests/test_energy.py
|
|
|
96
97
|
tests/test_geography.py
|
|
97
98
|
tests/test_gpu.py
|
|
98
99
|
tests/test_logging_output.py
|
|
100
|
+
tests/test_powermetrics.py
|
|
99
101
|
tests/test_ram.py
|
|
100
102
|
tests/test_tracking_inference.py
|
|
101
103
|
tests/test_viz_data.py
|
|
@@ -10,7 +10,7 @@ DEPENDENCIES = [
|
|
|
10
10
|
"requests",
|
|
11
11
|
"psutil",
|
|
12
12
|
"py-cpuinfo",
|
|
13
|
-
"
|
|
13
|
+
"rapidfuzz",
|
|
14
14
|
"click",
|
|
15
15
|
"prometheus_client",
|
|
16
16
|
]
|
|
@@ -20,7 +20,7 @@ TEST_DEPENDENCIES = ["mock", "pytest", "responses", "tox", "numpy", "requests-mo
|
|
|
20
20
|
|
|
21
21
|
setuptools.setup(
|
|
22
22
|
name="codecarbon",
|
|
23
|
-
version="2.3.
|
|
23
|
+
version="2.3.4",
|
|
24
24
|
author="Mila, DataForGood, BCG GAMMA, Comet.ml, Haverford College",
|
|
25
25
|
long_description=long_description,
|
|
26
26
|
long_description_content_type="text/markdown",
|
|
@@ -5,7 +5,12 @@ from unittest import mock
|
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
-
from codecarbon.core.cpu import
|
|
8
|
+
from codecarbon.core.cpu import (
|
|
9
|
+
TDP,
|
|
10
|
+
IntelPowerGadget,
|
|
11
|
+
IntelRAPL,
|
|
12
|
+
is_powergadget_available,
|
|
13
|
+
)
|
|
9
14
|
from codecarbon.core.units import Energy, Power, Time
|
|
10
15
|
from codecarbon.external.hardware import CPU
|
|
11
16
|
from codecarbon.input import DataSource
|
|
@@ -14,9 +19,10 @@ from codecarbon.input import DataSource
|
|
|
14
19
|
class TestIntelPowerGadget(unittest.TestCase):
|
|
15
20
|
@pytest.mark.integ_test
|
|
16
21
|
def test_intel_power_gadget(self):
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
if is_powergadget_available():
|
|
23
|
+
power_gadget = IntelPowerGadget()
|
|
24
|
+
cpu_details = power_gadget.get_cpu_details()
|
|
25
|
+
assert len(cpu_details) > 0
|
|
20
26
|
|
|
21
27
|
@mock.patch("codecarbon.core.cpu.IntelPowerGadget._log_values")
|
|
22
28
|
@mock.patch("codecarbon.core.cpu.IntelPowerGadget._setup_cli")
|
|
@@ -45,16 +51,16 @@ class TestIntelPowerGadget(unittest.TestCase):
|
|
|
45
51
|
"GT Frequency(MHz)": 125.0,
|
|
46
52
|
"GT Requsted Frequency(MHz)": 125.0,
|
|
47
53
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
if is_powergadget_available():
|
|
55
|
+
power_gadget = IntelPowerGadget(
|
|
56
|
+
output_dir=os.path.join(os.path.dirname(__file__), "test_data"),
|
|
57
|
+
log_file_name="mock_intel_power_gadget_data.csv",
|
|
58
|
+
)
|
|
59
|
+
cpu_details = power_gadget.get_cpu_details()
|
|
60
|
+
cpu_details["Cumulative IA Energy_0(mWh)"] = round(
|
|
61
|
+
cpu_details["Cumulative IA Energy_0(mWh)"], 3
|
|
62
|
+
)
|
|
63
|
+
self.assertDictEqual(expected_cpu_details, cpu_details)
|
|
58
64
|
|
|
59
65
|
|
|
60
66
|
class TestIntelRAPL(unittest.TestCase):
|
|
@@ -99,3 +99,34 @@ class TestCarbonTrackerConstant(unittest.TestCase):
|
|
|
99
99
|
with open(file_path, "r") as f:
|
|
100
100
|
lines = [line.rstrip() for line in f]
|
|
101
101
|
assert len(lines) == 2
|
|
102
|
+
|
|
103
|
+
def test_carbon_tracker_offline_region_error(self):
|
|
104
|
+
# Test errors when unknown information is given to the offline tracker
|
|
105
|
+
from codecarbon.external.geography import CloudMetadata
|
|
106
|
+
|
|
107
|
+
tracker = OfflineEmissionsTracker(
|
|
108
|
+
country_iso_code="USA",
|
|
109
|
+
cloud_provider="aws",
|
|
110
|
+
cloud_region="us-east-1",
|
|
111
|
+
output_dir=self.emissions_path,
|
|
112
|
+
output_file=self.emissions_file,
|
|
113
|
+
)
|
|
114
|
+
tracker.start()
|
|
115
|
+
tracker._measure_power_and_energy()
|
|
116
|
+
cloud: CloudMetadata = tracker._get_cloud_metadata()
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
with self.assertRaises(ValueError) as context:
|
|
120
|
+
tracker._emissions.get_cloud_country_iso_code(cloud)
|
|
121
|
+
self.assertTrue("Unable to find country name" in context.exception.args[0])
|
|
122
|
+
|
|
123
|
+
with self.assertRaises(ValueError) as context:
|
|
124
|
+
tracker._emissions.get_cloud_geo_region(cloud)
|
|
125
|
+
self.assertTrue("Unable to find country name" in context.exception.args[0])
|
|
126
|
+
|
|
127
|
+
with self.assertRaises(ValueError) as context:
|
|
128
|
+
tracker._emissions.get_cloud_country_name(cloud)
|
|
129
|
+
self.assertTrue("Unable to find country name" in context.exception.args[0])
|
|
130
|
+
|
|
131
|
+
finally:
|
|
132
|
+
tracker.stop()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import unittest
|
|
3
|
+
import unittest.result
|
|
4
|
+
from unittest import mock
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from codecarbon.core.powermetrics import ApplePowermetrics, is_powermetrics_available
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestApplePowerMetrics(unittest.TestCase):
|
|
12
|
+
@pytest.mark.integ_test
|
|
13
|
+
def test_apple_powermetrics(self):
|
|
14
|
+
if is_powermetrics_available():
|
|
15
|
+
power_gadget = ApplePowermetrics()
|
|
16
|
+
details = power_gadget.get_details()
|
|
17
|
+
assert len(details) > 0
|
|
18
|
+
|
|
19
|
+
@mock.patch("codecarbon.core.powermetrics.ApplePowermetrics._log_values")
|
|
20
|
+
@mock.patch("codecarbon.core.powermetrics.ApplePowermetrics._setup_cli")
|
|
21
|
+
def test_get_details(self, mock_setup, mock_log_values):
|
|
22
|
+
expected_details = {
|
|
23
|
+
"CPU Power": 0.3146,
|
|
24
|
+
"CPU Energy Delta": 0.3146,
|
|
25
|
+
"GPU Power": 0.0386,
|
|
26
|
+
"GPU Energy Delta": 0.0386,
|
|
27
|
+
}
|
|
28
|
+
if is_powermetrics_available():
|
|
29
|
+
powermetrics = ApplePowermetrics(
|
|
30
|
+
output_dir=os.path.join(os.path.dirname(__file__), "test_data"),
|
|
31
|
+
log_file_name="mock_powermetrics_log.txt",
|
|
32
|
+
)
|
|
33
|
+
cpu_details = powermetrics.get_details()
|
|
34
|
+
|
|
35
|
+
self.assertDictEqual(expected_details, cpu_details)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.3.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecarbon-2.3.2 → codecarbon-2.3.4}/carbonserver/carbonserver/api/routers/organizations.py
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
|
|
File without changes
|
{codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/private_infra/2016/canada_energy_mix.json
RENAMED
|
File without changes
|
|
File without changes
|
{codecarbon-2.3.2 → codecarbon-2.3.4}/codecarbon/data/private_infra/carbon_intensity_per_source.json
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
|
|
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
|