diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index eebce894a..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Bug Report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Python Version [e.g. 3.8] - - CodeCarbon Version [e.g. 2.1.4] - -**Additional context** -Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..26dc0d15a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,122 @@ +name: Bug report +description: Something in CodeCarbon is not working as expected +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to file a report. + + Before you start: if your emissions look wrong rather than the tool + crashing, please check the + [troubleshooting guide](https://docs.codecarbon.io/latest/how-to/troubleshooting/) + first — many measurement issues are configuration, especially RAPL + permissions on Linux. + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you expect, and what did you get instead? + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Minimal code to reproduce + description: The smallest script that shows the problem. + render: python + validations: + required: true + + - type: textarea + id: detect-output + attributes: + label: Output of `codecarbon detect` + description: | + Run `codecarbon detect` in the same environment and paste the full + output. This tells us what hardware and measurement backends + CodeCarbon found, and it answers most questions immediately. + render: shell + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Debug log + description: | + Re-run with `EmissionsTracker(log_level="debug")` and paste the + CodeCarbon log output. Warnings about RAPL, CPU detection, or + another running instance are especially relevant. + render: shell + validations: + required: false + + - type: input + id: version + attributes: + label: CodeCarbon version + description: "Output of `codecarbon --version`" + placeholder: "3.3.0" + validations: + required: true + + - type: input + id: python-version + attributes: + label: Python version + placeholder: "3.12.4" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: Operating system + options: + - Linux + - macOS (Apple Silicon) + - macOS (Intel) + - Windows + - Windows (WSL) + - Other + validations: + required: true + + - type: dropdown + id: environment + attributes: + label: Where is this running? + options: + - Directly on my machine + - Docker / container + - Virtual machine + - Cloud VM (AWS / GCP / Azure / other) + - HPC cluster / SLURM + - CI pipeline + - Jupyter notebook / Colab + - Other + validations: + required: true + + - type: dropdown + id: install-method + attributes: + label: How did you install CodeCarbon? + options: + - pip + - uv + - conda + pip + - from source + - other + validations: + required: true + + - type: textarea + id: context + attributes: + label: Anything else? + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..d6526b020 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false +contact_links: + - name: Question or help getting started + url: https://discord.gg/GS9js2XkJR + about: Ask on Discord — usually the fastest way to get an answer. + - name: Troubleshooting guide + url: https://docs.codecarbon.io/latest/how-to/troubleshooting/ + about: Common problems, error messages, and how to fix them. + - name: FAQ + url: https://docs.codecarbon.io/latest/explanation/faq/ + about: Accuracy, data sources, platform support, and more. + - name: Documentation + url: https://docs.codecarbon.io/ + about: Full documentation. diff --git a/.github/ISSUE_TEMPLATE/documentation_improvement.md b/.github/ISSUE_TEMPLATE/documentation_improvement.md deleted file mode 100644 index 7db54df18..000000000 --- a/.github/ISSUE_TEMPLATE/documentation_improvement.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Documentation Improvement -about: Suggest improvements or report issues in documentation -title: '' -labels: documentation -assignees: '' ---- - -**Where does the documentation need improvement?** -Provide the URL or section that needs improvement. - -**What needs to be changed?** -Describe what's currently wrong or what could be better. - -**Additional context** -Add any other context about the documentation issue here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation_improvement.yml b/.github/ISSUE_TEMPLATE/documentation_improvement.yml new file mode 100644 index 000000000..95b7d2bba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_improvement.yml @@ -0,0 +1,26 @@ +name: Documentation improvement +description: Report something missing, wrong, or confusing in the documentation +labels: ["documentation"] +body: + - type: input + id: location + attributes: + label: Which page? + description: URL or file path of the page that needs improvement. + placeholder: "https://docs.codecarbon.io/latest/how-to/configuration/" + validations: + required: true + + - type: textarea + id: problem + attributes: + label: What is wrong or missing? + validations: + required: true + + - type: textarea + id: suggestion + attributes: + label: What would you suggest instead? + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index c0e03d002..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Feature Request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000..d3cda3383 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,35 @@ +name: Feature request +description: Suggest a capability or improvement for CodeCarbon +labels: ["enhancement"] +body: + - type: textarea + id: goal + attributes: + label: What are you trying to accomplish? + description: | + Describe the goal rather than the implementation — what are you + measuring, and what makes it hard or impossible today? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: What would you like CodeCarbon to do? + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: What have you tried or considered? + description: Workarounds, other tools, or configuration you already tested. + validations: + required: false + + - type: textarea + id: context + attributes: + label: Anything else? + validations: + required: false diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 0ce0984f5..81c1df2ff 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,8 +1,17 @@ # Release drafter configuration https://github.com/release-drafter/release-drafter#configuration # Emojis were chosen to match the https://gitmoji.carloscuesta.me/ -name-template: "v$NEXT_PATCH_VERSION" -tag-template: "v$NEXT_PATCH_VERSION" +name-template: "v$RESOLVED_VERSION" +tag-template: "v$RESOLVED_VERSION" + +version-resolver: + major: + labels: [breaking] + minor: + labels: [enhancement, feature] + patch: + labels: [bug, bugfix, fix, refactoring, dependencies] + default: patch categories: - title: ":rocket: Features" diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..346304a85 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,62 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it using the metadata from this file." +title: "CodeCarbon: Estimate and track carbon emissions from computing" +abstract: >- + CodeCarbon estimates the electricity consumed by the hardware running your + code and converts it to CO2 emissions using the carbon intensity of the + local electricity grid. +type: software +authors: + - given-names: Benoit + family-names: Courty + - given-names: Victor + family-names: Schmidt + - given-names: Sasha + family-names: Luccioni + - given-names: Boris + family-names: Feld + - given-names: Jérémy + family-names: Lecourt + - given-names: Mathilde + family-names: Léval + - given-names: Luis + family-names: Blanche + - given-names: Alexis + family-names: Cruveiller + - given-names: Aditya + family-names: Joshi + - given-names: Alexis + family-names: Bogroff + - given-names: Hugues + family-names: de Lavoreille + - given-names: Niko + family-names: Laskaris + - given-names: Edoardo + family-names: Abati + - given-names: Douglas + family-names: Blank + - given-names: Ziyao + family-names: Wang + - given-names: Armin + family-names: Catovic + - given-names: Marc + family-names: Alencon + - given-names: Michał + family-names: Stęchły + - given-names: Christian + family-names: Bauer + - given-names: Lucas Otávio N. + family-names: de Araújo + - name: "The CodeCarbon contributors" +repository-code: "https://github.com/mlco2/codecarbon" +url: "https://codecarbon.io" +license: MIT +version: 3.3.0 +doi: 10.5281/zenodo.4658424 +date-released: 2026-08-04 +keywords: + - carbon emissions + - green AI + - sustainable computing + - energy consumption + - machine learning diff --git a/README.md b/README.md index a9aef3b32..cc1aa4073 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,9 @@ pip install codecarbon More installation options: [installation docs](https://docs.codecarbon.io/latest/how-to/installation/). +Something not working, or numbers that look wrong? See the +[troubleshooting guide](https://docs.codecarbon.io/latest/how-to/troubleshooting/). + ## Quickstart (Python) ```python @@ -78,9 +81,11 @@ We created a Python package that estimates your hardware electricity power consu CodeCarbon focuses on the main compute components it can measure or estimate directly: CPU, GPU, and RAM. It does not separately model disk I/O, network transfers, displays, cooling, or other peripherals because those sources are usually much smaller for local code-level experiments and are not exposed through the same low-overhead measurement interfaces. +On Linux, CodeCarbon reads Intel RAPL hardware energy counters when it can. If those counters are not readable it falls back to estimating from CPU load, which is less accurate — see [getting accurate CPU measurements](https://docs.codecarbon.io/latest/how-to/enable-rapl/) to enable them. + ![calculation Summary](docs/images/calculation.png) -We explain more about this calculation in the [**Methodology**](https://docs.codecarbon.io/latest/explanation/methodology/) section of the documentation. +We explain more about this calculation in the [**Methodology**](https://docs.codecarbon.io/latest/explanation/methodology/) section of the documentation, and we document how close those numbers are — and where they are not close — in [**Accuracy and validation**](https://docs.codecarbon.io/latest/explanation/accuracy/). ## Visualize @@ -97,9 +102,12 @@ You can visualize your experiment emissions on the [dashboard](https://dashboard | [CLI Tutorial](https://docs.codecarbon.io/latest/tutorials/cli/) | Track emissions from the command line | | [Python API Tutorial](https://docs.codecarbon.io/latest/tutorials/python-api/) | Track emissions in Python code | | [Comparing Model Efficiency](https://docs.codecarbon.io/latest/tutorials/comparing-model-efficiency/) | Measure carbon efficiency across ML models | +| [Accurate CPU measurements (Linux/RAPL)](https://docs.codecarbon.io/latest/how-to/enable-rapl/) | Read real energy counters instead of estimating | | [API Reference](https://docs.codecarbon.io/latest/reference/api/) | Full parameter documentation | | [Framework examples (scikit-learn)](https://docs.codecarbon.io/latest/how-to/scikit-learn/) | Task-oriented ML framework examples | | [Methodology](https://docs.codecarbon.io/latest/explanation/methodology/) | How emissions are calculated | +| [Accuracy and validation](https://docs.codecarbon.io/latest/explanation/accuracy/) | How accurate the numbers are, and why | +| [Alternatives comparison](https://docs.codecarbon.io/latest/explanation/alternatives/) | CodeCarbon vs other carbon tracking tools | | [When to use CodeCarbon vs EcoLogits](https://docs.codecarbon.io/latest/explanation/when-to-use/) | Choose the right tool | | [EcoLogits](https://ecologits.ai/) | Track emissions from GenAI API calls | | [Discord Community](https://discord.gg/GS9js2XkJR) | Chat with us and the community | @@ -126,29 +134,18 @@ Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR). ## Citation -If you find CodeCarbon useful for your research, you can find a citation under a variety of formats on [Zenodo](https://zenodo.org/records/11171501). - -
-BibTeX +If you find CodeCarbon useful for your research, use the **Cite this repository** button in the GitHub sidebar, or copy the BibTeX below. The DOI is a Zenodo concept DOI: it always resolves to the latest release. All versions and formats are on [Zenodo](https://doi.org/10.5281/zenodo.4658424). ```tex -@software{benoit_courty_2024_11171501, +@software{codecarbon, author = {Benoit Courty and Victor Schmidt and Sasha Luccioni and - Goyal-Kamal and - MarionCoutarel and Boris Feld and Jérémy Lecourt and - LiamConnell and - Amine Saboni and - Inimaz and - supatomic and Mathilde Léval and Luis Blanche and Alexis Cruveiller and - ouminasara and - Franklin Zhao and Aditya Joshi and Alexis Bogroff and Hugues de Lavoreille and @@ -161,20 +158,15 @@ If you find CodeCarbon useful for your research, you can find a citation under a Michał Stęchły and Christian Bauer and Lucas Otávio N. de Araújo and - JPW and - MinervaBooks}, - title = {mlco2/codecarbon: v2.4.1}, - month = may, - year = {2024}, + {The CodeCarbon contributors}}, + title = {CodeCarbon: Estimate and track carbon emissions from computing}, publisher = {Zenodo}, - version = {v2.4.1}, - doi = {10.5281/zenodo.11171501}, - url = {https://doi.org/10.5281/zenodo.11171501} + version = {3.3.0}, + doi = {10.5281/zenodo.4658424}, + url = {https://doi.org/10.5281/zenodo.4658424} } ``` -
- ## Contact Feel free to chat with us on [Discord](https://discord.gg/GS9js2XkJR). diff --git a/carbonserver/carbonserver/api/infra/api_key_utils.py b/carbonserver/carbonserver/api/infra/api_key_utils.py index 4ecfe2e08..d71a7de13 100644 --- a/carbonserver/carbonserver/api/infra/api_key_utils.py +++ b/carbonserver/carbonserver/api/infra/api_key_utils.py @@ -13,8 +13,13 @@ def generate_api_key() -> str: return prefixed_api_key -def get_api_key_hash(api_key: str) -> str: - """Get the hash of the api key""" +def get_api_key_hash(api_key: str) -> bytes: + """Get the hash of the api key. + + Returns bcrypt's own ``bytes`` output. It is stored in a ``String`` column and + comes back from the database as ``str``, which is what ``verify_api_key`` + expects — hence the asymmetric annotations. + """ return bcrypt.hashpw( api_key.encode(), bcrypt.gensalt(), @@ -32,7 +37,19 @@ def verify_api_key(plain_api_key: str, hashed_api_key: str) -> bool: def generate_lookup_value(api_key: str) -> str: - # Generate a SHA-256 hash of the API key + """Derive the non-secret database index for an API key. + + This is a lookup shortcut, not a credential. It narrows a token lookup to a + handful of candidate rows; authentication is always ``verify_api_key`` + (bcrypt) against the stored ``hashed_token``. Collisions are expected and the + caller iterates over every candidate, so 8 hex characters is deliberate. + + SHA-256 is correct here and must not be swapped for an HMAC or a KDF: static + analysis flags this line as weak credential hashing (``py/weak-sensitive-data- + hashing``), but the value authenticates nothing. Changing the derivation would + also be unrecoverable — it is computed from plaintext tokens that are never + stored, so existing rows could not be backfilled and every issued API key + would stop resolving. + """ sha256_hash = hashlib.sha256(api_key.encode()).hexdigest() - # Use the first 8 characters of the hash as a lookup value return sha256_hash[:8] diff --git a/codecarbon/core/cpu.py b/codecarbon/core/cpu.py index e21c39fd8..2c44ff9af 100644 --- a/codecarbon/core/cpu.py +++ b/codecarbon/core/cpu.py @@ -28,6 +28,13 @@ # default W value per core for a CPU if no model is found in the ref csv DEFAULT_POWER_PER_CORE = 4 +_TROUBLESHOOTING_URL = "https://docs.codecarbon.io/latest/how-to/troubleshooting/" +RAPL_PERMISSION_HELP = ( + "You can grant read permission with: sudo chmod -R a+r /sys/class/powercap/* " + "(this does not persist across reboots, see " + f"{_TROUBLESHOOTING_URL}#rapl-permission-denied for a permanent setup)" +) + @lru_cache(maxsize=1) def is_powergadget_available() -> bool: @@ -175,18 +182,16 @@ def warn_permission_denied(energy_path: str): nonlocal already_warned if not already_warned: logger.warning( - "\tRAPL - Permission denied reading RAPL file %s. " - "You can grant read permission with: " - "sudo chmod -R a+r /sys/class/powercap/*", + "\tRAPL - Permission denied reading RAPL file %s. %s", energy_path, + RAPL_PERMISSION_HELP, ) already_warned = True else: logger.debug( - "\tRAPL - Permission denied reading RAPL file %s. " - "You can grant read permission with: " - "sudo chmod -R a+r /sys/class/powercap/*", + "\tRAPL - Permission denied reading RAPL file %s. %s", energy_path, + RAPL_PERMISSION_HELP, ) return warn_permission_denied @@ -583,8 +588,7 @@ def _validate_domain_readable( return True, is_required_main except PermissionError: msg = f"\tRAPL - Permission denied reading RAPL file {rapl_file}." - suggestion = "You can grant read permission with: sudo chmod -R a+r /sys/class/powercap/*" - logger.warning("%s %s; skipping.", msg, suggestion) + logger.warning("%s %s; skipping.", msg, RAPL_PERMISSION_HELP) return False, False except Exception as e: logger.debug( @@ -1009,9 +1013,10 @@ def _main(self) -> Tuple[str, int]: ) return cpu_model_detected, power logger.warning( - "We saw that you have a %s but we don't know it." - + " Please contact us.", + "We saw that you have a %s but we don't know it. " + "Please help us add it, see %s#unknown-cpu-model", cpu_model_detected, + _TROUBLESHOOTING_URL, ) if is_psutil_available(): # Count thread of the CPU diff --git a/codecarbon/emissions_tracker.py b/codecarbon/emissions_tracker.py index 96ed00c91..368f06917 100644 --- a/codecarbon/emissions_tracker.py +++ b/codecarbon/emissions_tracker.py @@ -686,6 +686,22 @@ def get_detected_hardware(self) -> Dict[str, Any]: return hardware_info def service_shutdown(self, signum, frame): + """ + Signal handler that stops the tracker on SIGTERM/SIGINT, for use when + CodeCarbon runs as a long-lived service. Register it with: + + ```py + import signal + + tracker = EmissionsTracker() + signal.signal(signal.SIGTERM, tracker.service_shutdown) + signal.signal(signal.SIGINT, tracker.service_shutdown) + ``` + + :param signum: Signal number, passed by `signal.signal` + :param frame: Current stack frame, passed by `signal.signal` + :return: None + """ logger.warning("service_shutdown - Caught signal %d" % signum) self.stop() @@ -1600,7 +1616,7 @@ def track_emissions( def _decorate(fn: Callable): @wraps(fn) - def wrapped_fn(*args, **kwargs): + def _wrapped_fn(*args, **kwargs): fn_result = None # Handle backward compatibility for co2_signal_api_token @@ -1699,7 +1715,7 @@ def wrapped_fn(*args, **kwargs): logger.info("Done!\n") return fn_result - return wrapped_fn + return _wrapped_fn if fn: return _decorate(fn) @@ -1711,9 +1727,9 @@ def track_task_emissions( ): """ Decorator to track emissions specific to a task. With a tracker as input, it will add task emissions to global emissions. - :param: tracker: global tracker used in the current execution. If none is provided, instanciates an emission + :param tracker: global tracker used in the current execution. If none is provided, instanciates an emission tracker which will read default parameter from config to enable tracking - :param: task_name: Task to be tracked. If none is provided, an id will be used. + :param task_name: Task to be tracked. If none is provided, an id will be used. :return: The decorated function """ @@ -1725,7 +1741,7 @@ def track_task_emissions( def _decorate(fn: Callable[..., Any]) -> Callable[..., Any]: @wraps(fn) - def wrapped_fn(*args, **kwargs): + def _wrapped_fn(*args, **kwargs): fn_result = None tracker.start_task(task_name=task_name) try: @@ -1741,7 +1757,7 @@ def wrapped_fn(*args, **kwargs): logger.info("Done!\n") return fn_result - return wrapped_fn + return _wrapped_fn if fn: return _decorate(fn) diff --git a/docs/explanation/accuracy.md b/docs/explanation/accuracy.md new file mode 100644 index 000000000..8b3d3bf53 --- /dev/null +++ b/docs/explanation/accuracy.md @@ -0,0 +1,168 @@ +# Accuracy and validation + +This page describes what CodeCarbon measures, how close those measurements are +to a hardware reference, where the remaining error comes from, and what you can +do to reduce it. + +## What is inside the measurement boundary + +CodeCarbon reports the **direct electricity consumption of the compute +components it can read or estimate**: CPU, GPU and RAM. It then multiplies the +resulting energy by the carbon intensity of the local electricity grid. + +Outside the boundary: + +- disk I/O, network transfers, displays, cooling and other peripherals; +- power supply and datacenter overhead, unless you set a + [PUE](../how-to/configuration.md) value yourself; +- life-cycle (embodied) emissions of the hardware. + +Every accuracy statement below is scoped to that boundary. A CodeCarbon figure +is not a whole-facility footprint, and it is not meant to be one. + +## How accurate each measurement backend is + +CodeCarbon prefers hardware energy counters and falls back to estimation when no +counter is readable. The fallback order is documented in +[Methodology → CPU metrics priority](methodology.md#cpu-metrics-priority). + +| Backend | How it works | Agreement with a reference | When it is used | +|---|---|---|---| +| Intel RAPL | Reads hardware energy counters under `/sys/class/powercap/` | On the four CPUs profiled in this repository, CodeCarbon's RAPL readings matched `stress-ng --rapl` on the same machine. RAPL is itself an on-die estimate and its own absolute error is not characterised here. | Linux, Intel and AMD (kernel ≥ 5.8), when the counters are readable | +| NVML (NVIDIA) | Reads accumulated board energy from the driver (`nvmlDeviceGetTotalEnergyConsumption`) | Not yet measured against an external reference | Any NVIDIA GPU with a working driver | +| amdsmi (AMD) | Reads the driver energy counter (`amdsmi_get_energy_count`) | Not yet measured against an external reference | AMD GPUs | +| macOS `powermetrics` | System power reporting | Not yet measured against an external reference | macOS, Intel and Apple Silicon | +| Windows EMI | Energy Meter Interface | Not yet measured against an external reference | Windows 11, where the platform exposes it | +| Intel Power Gadget | Vendor tool, deprecated upstream | Not yet measured | Legacy path | +| CPU load × TDP | Estimates power from CPU utilisation against the TDP listed in `cpu_power.csv` | See the profiling results below: on the machines profiled, the estimate deviated from RAPL by roughly −60% to +90% depending on CPU and load point | Fallback when no CPU counter is available | +| Default watts per thread | Estimates from thread count alone | Not characterised; this is the least accurate path | Last resort, when the CPU model is absent from `cpu_power.csv` | + +### The CPU load × TDP fallback, measured + +`examples/compare_cpu_load_and_RAPL.py` sweeps CPU load with `stress-ng` and +records, at each load point, both the RAPL reading and the TDP-based estimate +CodeCarbon would have produced. The raw sweeps live in +`codecarbon/data/hardware/cpu_load_profiling/` and are plotted in +`examples/compare_cpu_load_and_RAPL.ipynb`. Runs are dated January 2025. + +Deviation of the estimate from the RAPL reading, over load points above 5% +(negative means the estimate is lower than RAPL): + +| CPU | At full load | Range across load points | +|---|---|---| +| Dual Intel Xeon E5-2620 v3 (24 threads) | +40% | +2% to +59% | +| Intel Xeon E3-1240 v2 (8 threads) | +37% | +20% to +187% | +| AMD Ryzen Threadripper 1950X (32 threads) | +3% | −57% to +18% | +| AMD EPYC 8024P (16 threads) | +88% | −50% to +88% | + +Two things drive the error. + +**The TDP is not the real power ceiling.** CodeCarbon assumes a CPU at 100% load +draws its full rated TDP. On the dual E5-2620 v3, the database TDP implies 170 W +for the pair, while RAPL reported about 117 W at full load — the chips are held +near their base frequency and never reach the rated figure. On the EPYC 8024P +the gap is larger still. + +**The relationship between load and power is not linear.** CodeCarbon +interpolates linearly between idle and TDP. Real curves are convex on some parts +(the E3-1240 v2 stays under 10 W up to 40% load, so the linear estimate +overshoots it by well over 100%) and saturate early on others (the Threadripper +reaches its power ceiling around 65% load, so the estimate *undershoots* at mid +load). + +The practical reading: the fallback gets the order of magnitude right and can be +off by a factor of two in either direction on a specific machine and workload. +It is not a substitute for RAPL. If your numbers need to be defensible, +[enable RAPL](../how-to/enable-rapl.md). + +### Wall-socket comparison + +The Threadripper sweeps also record whole-machine power from a smart plug +(`tapo_power` in the CSV files). Those figures are not directly comparable to +the CPU numbers — they include the GPU, disks, fans and power supply losses, and +the machine drew about 115 W at idle. They are published for completeness, not as +a validation of the CPU figures. + +A proper wall-socket validation — a controlled comparison of a wattmeter against +CodeCarbon's reported energy for the same interval, on the same machine — has +not been done. It is the reference method reviewers ask about, and no amount of +RAPL-versus-estimate analysis substitutes for it, because RAPL is itself an +instrument with its own error. Contributions are welcome. + +## Where the error comes from + +Three independent error sources compound. Which one dominates depends on your +setup, and the remedy differs for each. + +**Power measurement.** Covered by the table above. Small when hardware counters +are available, potentially a factor of two when the TDP fallback is in use. + +**Carbon intensity.** Often the largest term. When CodeCarbon has no data for +your country it falls back to a world average of 475 gCO₂eq/kWh. Real national +intensities span from under 50 gCO₂eq/kWh to over 700, so this default can be +wrong by close to an order of magnitude. Marginal versus average intensity, and +hourly versus annual averages, add further uncertainty that CodeCarbon does not +model. See [Methodology](methodology.md) for the data sources. + +**Temporal resolution.** CodeCarbon samples every `measure_power_secs` seconds +(default 15). Energy counters accumulate between samples, so this does not lose +energy on the counter paths; but on the estimation path, and for workloads +shorter or spikier than the interval, the sampled load is a poor summary of what +actually happened. + +## What to do about it + +In rough order of impact: + +1. **Enable RAPL on Linux** — see [Improve measurement accuracy with + RAPL](../how-to/enable-rapl.md). This is the single largest improvement + available on most machines, and it moves you from estimation to measurement. +2. **Set your country, region and cloud provider correctly** in the + [configuration](../how-to/configuration.md). A wrong region is usually a + larger error than a wrong power reading. +3. **Set `pue`** if you run in a datacenter whose overhead you know. +4. **Contribute your CPU model** to + `codecarbon/data/hardware/cpu_power.csv` if CodeCarbon logs that it does not + know your CPU. That moves you off the default-watts-per-thread path. +5. **Measure longer runs.** Short runs are dominated by sampling noise and + tracker startup. +6. **Compare within one machine, not across machines.** Relative comparisons + (this model versus that model, on the same hardware and backend) are far more + trustworthy than absolute totals. + +## Reproduce this yourself + +Nothing here needs to be taken on trust. + +- `examples/compare_cpu_load_and_RAPL.py` — runs the load sweep and writes a CSV + with RAPL and estimated power side by side. Requires `stress-ng` and readable + RAPL counters. +- `examples/compare_cpu_load_and_RAPL.ipynb` — plots the CSVs, including the + sweeps committed under `codecarbon/data/hardware/cpu_load_profiling/`. +- `examples/rapl/` — diagnostic scripts for inspecting RAPL domains + (`intel_rapl_show.py`, `test_rapl_domains.py`, `test_dram_option.py` and + others). +- `examples/test_rapl_calculus.sh` — a shell check of the RAPL energy + calculation. +- `examples/print_hardware.py` — shows which backend CodeCarbon selected on your + machine, which tells you which row of the table above applies to you. + +If you run the sweep on a CPU that is not yet profiled, a pull request adding +the CSV to `codecarbon/data/hardware/cpu_load_profiling/` is a directly useful +contribution. + +## Known gaps + +Stated plainly, because a validation page that only lists strengths is not a +validation page: + +- No external wattmeter validation of any backend. +- GPU and RAM backends are not validated against an independent reference. +- The RAPL-versus-estimate comparison covers four CPUs, all Linux, none of them + recent. +- No uncertainty interval is attached to reported emissions figures. +- On the dual E5-2620 v3, RAPL reported markedly lower package power when the + same total load was spread over fewer cores than over all cores. The notebook + flags this as unexpected and unexplained; it was reproduced on a second run. + + diff --git a/docs/explanation/alternatives.md b/docs/explanation/alternatives.md new file mode 100644 index 000000000..db712f2b4 --- /dev/null +++ b/docs/explanation/alternatives.md @@ -0,0 +1,161 @@ +# CodeCarbon and the alternatives + +Several tools measure or estimate the energy and carbon footprint of computing. +They overlap, but they were built for different jobs, and for some of the +questions below another tool is the better answer. This page tries to say so +plainly. + +For the CodeCarbon versus EcoLogits question specifically — local hardware +versus remote GenAI APIs — see [When to use +CodeCarbon](when-to-use.md). The two are complementary rather than competing. + +## At a glance + +| Tool | Type | Language | Licence | Power source | Distinguishing capability | +|---|---|---|---|---|---| +| **CodeCarbon** | Library + CLI + hosted dashboard | Python | MIT | RAPL, NVML, amdsmi, macOS `powermetrics`, Windows EMI; TDP × load estimate as fallback | Per-task attribution, offline mode, several output backends | +| [ML CO2 Impact](https://mlco2.github.io/impact/) | Web calculator | — (web) | MIT | None — you enter hardware, hours and region | Estimate *before* you run; same `mlco2` GitHub organisation as CodeCarbon | +| [carbontracker](https://github.com/lfwa/carbontracker) | Library | Python | MIT | RAPL, NVML | Predicts a run's total footprint from its first epochs | +| [eco2AI](https://github.com/sb-ai-lab/Eco2AI) | Library | Python | Apache-2.0 | RAPL, NVML | Its own regional emission-intensity dataset | +| [experiment-impact-tracker](https://github.com/Breakend/experiment-impact-tracker) | Library | Python | MIT | RAPL, NVML | The original tool in this space; widely cited | +| [Zeus](https://github.com/ml-energy/zeus) | Library | Python | Apache-2.0 | NVML, RAPL | Energy/time trade-off optimisation, not only reporting | +| [Scaphandre](https://github.com/hubblo-org/scaphandre) | Agent / exporter | Rust | Apache-2.0 | RAPL | Host- and container-level metering, Prometheus-native | +| Cloud provider tools | Vendor console | — | proprietary | Provider-internal | Whole-account totals at billing granularity | + +A note on `experiment-impact-tracker`: its GitHub repository is archived, with no +commits since January 2024. It remains worth knowing about, because many people +find the paper first and then look for the tool. If that is you, CodeCarbon, +carbontracker and eco2AI all cover the same ground and are receiving updates. + +The table deliberately has no per-hardware yes/no columns. Almost every tool +answers "yes, if the counters are readable", so a grid of qualified yeses carries +no information. It also has no maintenance column, for the reason above: the +facts belong in prose with dates attached. + +## Which one should you use? + +### "I want to know the cost before I run it" + +Use **ML CO2 Impact** or **carbontracker**. This is not what CodeCarbon does. + +ML CO2 Impact is a web form: you pick a GPU, a region and a number of hours, and +it gives you an estimate plus a LaTeX snippet for your paper. Nothing to install, +nothing to instrument. + +carbontracker measures the first epochs of a training run and extrapolates to the +full schedule, which lets you decide whether to continue before you have spent +the compute. If "should I run this at all" is your question, that is the right +shape of tool. + +CodeCarbon reports what a run actually consumed, during and after the fact. + +### "I want to attribute emissions to a specific function or training phase" + +Use **CodeCarbon**. `tracker.start_task("name")` and `tracker.stop_task()` split +a single process into separately reported segments, so you can compare +preprocessing against training against inference inside one script. See +`examples/task_inference.py` and `examples/task_loop_same_task.py`. + +### "I want to reduce energy use, not just report it" + +Look at **Zeus** first. It comes from a systems-research angle and is built +around finding a good point on the energy/time trade-off curve — batch size, +power limit, GPU frequency — rather than around producing a footprint report. +CodeCarbon will tell you what a configuration costs; it will not search for a +better one. + +### "I want to monitor a fleet, not a script" + +**Scaphandre** is likely the better fit. It is an agent, not a library: it meters +whole hosts and containers without any change to the applications running on +them, and it exports to Prometheus natively. If your goal is a +datacenter-or-cluster dashboard and you are not instrumenting individual jobs, +start there. + +CodeCarbon can do fleet work, but it approaches it from the other direction — it +starts inside the process and scales outward. If you want per-job attribution +*and* fleet aggregation, it has: + +- a [Prometheus output](../how-to/logging.md) (`examples/prometheus_call.py`); +- a [Linux service deployment mode](../how-to/linux-service.md) that monitors a + whole machine without instrumenting code; +- an [Ansible playbook](../how-to/ansible.md) for rolling that out; +- a hosted API and dashboard that aggregates across machines and projects. + +### "I'm on SLURM or an HPC cluster" + +CodeCarbon has a dedicated [SLURM guide](../how-to/slurm.md) covering multi-node +runs, plus ROCm and PyTorch examples under `examples/slurm_rocm/`. Most of the +alternatives leave the multi-node aggregation problem to you. + +### "I need the total for my AWS or GCP account" + +Use the provider's own tool: the AWS Customer Carbon Footprint Tool, Google Cloud +Carbon Footprint, or the Azure Emissions Impact Dashboard. They have access to +data no external library can see — actual facility PUE, the provider's own +energy contracts — and they cover your whole account, including services no +Python library can instrument. + +They will not answer "what did *this training run* cost", because they report at +billing-account granularity with monthly latency. The two are complementary: the +provider console for reporting obligations, CodeCarbon for attributing a number +to a specific experiment. + +### "I need something I can cite" + +carbontracker, eco2AI and experiment-impact-tracker each have an accompanying +academic paper, and so does the methodology behind CodeCarbon. CodeCarbon also +has a Zenodo DOI: use the **Cite this repository** button in the GitHub sidebar, +or the BibTeX entry in the repository README. See also the +[Methodology](methodology.md) page. If a reviewer asks how accurate the numbers +are, [Accuracy and validation](accuracy.md) is the page to point at — including +its list of known gaps. + +### "I'm not on Linux" + +CodeCarbon reads `powermetrics` on macOS (Intel and Apple Silicon) and the Energy +Meter Interface on Windows 11, falling back to the TDP estimate elsewhere. Most +of the alternatives are Linux-first and rely on RAPL. If you are on a Mac or a +Windows machine, check platform support carefully whichever tool you pick — and +read the [accuracy page](accuracy.md), because a fallback estimate on any of +these tools is an estimate. + +## What CodeCarbon is actually good at + +Summarised, since the sections above are organised around other tools: + +- **Breadth of measurement backends** with a documented fallback order — RAPL, + NVML, amdsmi, `powermetrics`, Windows EMI, then TDP-based estimation. See + [CPU metrics priority](methodology.md#cpu-metrics-priority). +- **Task-level attribution** within a single process. +- **Offline mode** (`OfflineEmissionsTracker`) for air-gapped machines, with no + no calls to the CodeCarbon API. +- **Output flexibility** — CSV, HTTP endpoint, logger, Prometheus, Logfire, + BOAMPS, and the hosted API. +- **A hosted dashboard**, if you do not want to build the aggregation layer + yourself. +- **A CLI**, so you can measure a process without writing any Python. + +## Where CodeCarbon is the wrong choice + +- You want a footprint estimate for a run you have not done yet → ML CO2 Impact, + or carbontracker's prediction mode. +- You want to *optimise* energy use rather than report it → Zeus. +- You want host-level metering with no application changes and a Prometheus + dashboard → Scaphandre. +- You are measuring calls to a hosted LLM API rather than local compute → + [EcoLogits](when-to-use.md). +- You need an auditable total for a cloud account → the provider's own tool. + +## Corrections welcome + +Comparison pages go stale, and one that is wrong about another project is worse +than no page at all. If something here misrepresents a tool you work on, please +[open an issue](https://github.com/mlco2/codecarbon/issues) and it will be +fixed. + + diff --git a/docs/explanation/faq.md b/docs/explanation/faq.md index 624ebd90e..afd207a7d 100644 --- a/docs/explanation/faq.md +++ b/docs/explanation/faq.md @@ -6,7 +6,25 @@ Use **CodeCarbon** when you run code on hardware you control—training models, ## How accurate are your estimations? -It is hard to quantify the entirety of computing emissions, because there are many factors in play, notably the life-cycle emissions of computing infrastructure. We therefore only focus on the direct emissions produced by running the actual code, but recognize that there is much work to be done to improve this estimation. +It depends on which measurement backend your machine offers. With hardware energy counters (RAPL on Linux, NVML or amdsmi for GPUs) CodeCarbon reads real energy consumption. Without them it falls back to estimating CPU power from load and TDP, which on the machines we profiled deviated from RAPL by up to roughly a factor of two in either direction. Carbon intensity is a separate and often larger error source: without regional data CodeCarbon uses a world average of 475 gCO2.eq/kWh. + +We also only cover the direct emissions of running the code — CPU, GPU and RAM — and not the life-cycle emissions of the hardware. + +See [Accuracy and validation](accuracy.md) for the measured figures, the known gaps, and how to improve your own numbers. + +## How does CodeCarbon compare to other carbon tracking tools? + +See [CodeCarbon and the alternatives](alternatives.md), which covers carbontracker, eco2AI, experiment-impact-tracker, Zeus, Scaphandre, ML CO2 Impact and cloud provider tooling — including the cases where one of those is the better choice. + +Accuracy also depends on your own machine. On Linux, CodeCarbon reads real CPU energy counters when they are readable, and otherwise estimates from CPU load and the processor's TDP, which is materially less accurate. See the next question. + +## Why are my measurements estimates instead of real readings? + +On Linux, CodeCarbon reads the Intel RAPL hardware energy counters under `/sys/class/powercap`. Since a kernel security fix these files are root-only by default, so CodeCarbon often cannot read them and falls back to estimating CPU power from CPU load and the processor's TDP. You will see a `RAPL - Permission denied` warning in the logs when this happens, but the warning is easy to miss in a notebook, a training framework that reconfigures logging, or a CI job. + +The fix takes about two minutes and persists across reboots: see [Improve Measurement Accuracy with RAPL](../how-to/enable-rapl.md). The one-line `sudo chmod -R a+r /sys/class/powercap/*` also works but is reset on the next restart. + +Note that RAPL counters do not exist at all in most containers and virtual machines, so estimation is expected there. ## What are the sources of your energy carbon intensity data? @@ -55,7 +73,10 @@ CodeCarbon supports various CPU architectures, GPUs, and cloud providers. For de ## How do I report a bug? -Please open an issue on [GitHub](https://github.com/mlco2/codecarbon/issues) with: +First check the [Troubleshooting](../how-to/troubleshooting.md) guide — most +warnings CodeCarbon prints are explained there, along with the fix. + +If it is still a bug, please open an issue on [GitHub](https://github.com/mlco2/codecarbon/issues) with: - Your environment details - Steps to reproduce - Expected vs actual behavior diff --git a/docs/explanation/methodology.md b/docs/explanation/methodology.md index fb51122d0..58f37f8da 100644 --- a/docs/explanation/methodology.md +++ b/docs/explanation/methodology.md @@ -229,16 +229,18 @@ the option is enabled. Legacy support for `Intel Power Gadget` is kept for machines where it is -still installed, but the tool [has been discontinued by -Intel](https://github.com/mlco2/codecarbon/issues/457). +still installed, but the tool has been +[discontinued by Intel](https://www.intel.com/content/www/us/en/developer/articles/tool/power-gadget.html) +(known limitation, tracked in +[issue #457](https://github.com/mlco2/codecarbon/issues/457)). - **On Mac (Intel)** Tracks Intel processors energy consumption using the `Intel Power Gadget`. You need to install it yourself from this [source](https://www.intel.com/content/www/us/en/developer/articles/tool/power-gadget.html) -. But has been discontinued. There is a discussion about it on [github -issues #457](https://github.com/mlco2/codecarbon/issues/457). +. Intel has since discontinued the tool; this is a known limitation, +tracked in [issue #457](https://github.com/mlco2/codecarbon/issues/457). - **Apple Silicon Chips (M1, M2, M3, ...)** @@ -248,9 +250,10 @@ Codecarbon tracks Apple Silicon Chip energy consumption using `powermetrics`. It should be available natively on any mac. However, this tool is only usable with `sudo` rights and to our current knowledge, there are no other options to track the energy consumption of -the Apple Silicon Chip without administrative rights (if you know of any -solution for this do not hesitate and [open an issue with your proposed -solution](https://github.com/mlco2/codecarbon/issues/)). +the Apple Silicon Chip without administrative rights. This is a known +limitation; if you know of any solution, please +[open an issue](https://github.com/mlco2/codecarbon/issues) with your +proposed approach. To give sudo rights without having to enter a password each time, you can modify the sudoers file with the following command: @@ -304,8 +307,12 @@ will read two files : RAPL (Running Average Power Limit) is a feature of modern processors that provides energy consumption measurements through hardware counters. -See for more -information. +For the primary sources, see Khan et al., *RAPL in Action: Experiences in +Using RAPL for Power Measurements*, and Weaver's +[Reading RAPL energy measurements from Linux](https://web.eece.maine.edu/~vweaver/projects/rapl/) +(both listed under [References](#references)). This +[blog post](https://blog.chih.me/read-cpu-power-with-RAPL.html) is a useful +informal walkthrough. Despite the name "Intel RAPL", it supports AMD processors since Linux kernel 5.8. @@ -343,12 +350,7 @@ We compute energy consumption as the product of the power consumed and the time the power was consumed for. The formula is: `Energy = Power * Time` -## References - -[Energy Usage Reports: Environmental awareness as part of algorithmic -accountability](https://arxiv.org/pdf/1911.08354.pdf) - -### How CodeCarbon Works +## How CodeCarbon Works CodeCarbon uses a scheduler that, by default, calls for a measure every 15 seconds, so it has no significant overhead. @@ -433,3 +435,64 @@ These estimates are approximate and subject to regional variations in: The emission factors used are defined in the [CodeCarbon source code](https://github.com/mlco2/codecarbon/blob/master/webapp/src/helpers/constants.ts). They are based on publicly available data and general assumptions. + +## References + +### Foundational work + +Strubell, E., Ganesh, A., & McCallum, A. (2019). *Energy and Policy +Considerations for Deep Learning in NLP*. Proceedings of ACL 2019. + + +Lacoste, A., Luccioni, A., Schmidt, V., & Dandres, T. (2019). *Quantifying +the Carbon Emissions of Machine Learning*. NeurIPS Workshop on Tackling +Climate Change with Machine Learning. + + +Lottick, K., Susai, S., Friedler, S. A., & Wilson, J. P. (2019). *Energy +Usage Reports: Environmental awareness as part of algorithmic +accountability*. NeurIPS Workshop on Tackling Climate Change with Machine +Learning. + +Henderson, P., Hu, J., Romoff, J., Brunskill, E., Jurafsky, D., & Pineau, J. +(2020). *Towards the Systematic Reporting of the Energy and Carbon Footprints +of Machine Learning*. Journal of Machine Learning Research, 21(248). + + +Patterson, D., Gonzalez, J., Le, Q., Liang, C., Munguia, L.-M., +Rothchild, D., So, D., Texier, M., & Dean, J. (2021). *Carbon Emissions and +Large Neural Network Training*. + +Luccioni, A. S., Viguier, S., & Ligozat, A.-L. (2022). *Estimating the Carbon +Footprint of BLOOM, a 176B Parameter Language Model*. + + +### Hardware measurement + +Khan, K. N., Hirki, M., Niemi, T., Nurminen, J. K., & Ou, Z. (2018). +*RAPL in Action: Experiences in Using RAPL for Power Measurements*. +ACM Transactions on Modeling and Performance Evaluation of Computing Systems. + +Weaver, V. M. *Reading RAPL energy measurements from Linux*. + + +Microsoft. *Energy Meter Interface (EMI) driver documentation*. + + +### Carbon intensity data sources + +Our World in Data. *Carbon intensity of electricity generation*. + + +International Energy Agency (2019). *Global Energy & CO2 Status Report*. + + +Google Cloud. *Carbon free energy for Google Cloud regions*. + + +### Citing CodeCarbon + +See [CITATION.cff](https://github.com/mlco2/codecarbon/blob/master/CITATION.cff) +or the "Cite this repository" button on GitHub. The +[README](https://github.com/mlco2/codecarbon#citation) also carries a ready-made +BibTeX entry. diff --git a/docs/explanation/when-to-use.md b/docs/explanation/when-to-use.md index 83366037a..5acdb681e 100644 --- a/docs/explanation/when-to-use.md +++ b/docs/explanation/when-to-use.md @@ -21,3 +21,7 @@ CodeCarbon measures actual power consumption (CPU, GPU, RAM) and converts it to ## Both are complementary Use CodeCarbon for training and local inference. Use EcoLogits for remote API inference. Together they cover the full lifecycle of AI workloads. + +## Other tools + +For a comparison with carbontracker, eco2AI, experiment-impact-tracker, Zeus, Scaphandre, ML CO2 Impact and cloud provider tooling, see [CodeCarbon and the alternatives](alternatives.md). diff --git a/docs/how-to/agent-instructions.md b/docs/how-to/agent-instructions.md index 8b3ebfd0e..3327ed6cd 100644 --- a/docs/how-to/agent-instructions.md +++ b/docs/how-to/agent-instructions.md @@ -159,7 +159,7 @@ uv run task -l - **[carbonserver/tests/TESTING.md](https://github.com/mlco2/codecarbon/blob/master/carbonserver/tests/TESTING.md)**: Comprehensive testing guide ### VS Code Debugging -The repository includes VS Code launch configurations in `docs/how-to/contributing.md` for: +The repository includes VS Code launch configurations in `docs/how-to/development.md` for: - Debugging current Python file - Running pytest with debugger - Testing codecarbon CLI monitor diff --git a/docs/how-to/configuration.md b/docs/how-to/configuration.md index 9f6766aa1..500bd4bda 100644 --- a/docs/how-to/configuration.md +++ b/docs/how-to/configuration.md @@ -120,7 +120,8 @@ EmissionsTracker(electricitymaps_api_token="your-token-here") The old parameter name `co2_signal_api_token` still works for backward compatibility but is deprecated and will be removed in a future version. - Use `electricitymaps_api_token` instead. + Use `electricitymaps_api_token` instead. See the + [deprecations list](https://docs.codecarbon.io/latest/reference/deprecations/). ## Tracking Mode diff --git a/docs/how-to/contributing.md b/docs/how-to/contributing.md index 243bb80d2..827e3486f 100644 --- a/docs/how-to/contributing.md +++ b/docs/how-to/contributing.md @@ -1,88 +1,32 @@ -# Contributing to Code Carbon +# Contributing to CodeCarbon -(New to open-source? [Here's a guide to help you](https://opensource.guide/how-to-contribute/)) +New to open source? [Here's a guide to help you](https://opensource.guide/how-to-contribute/). +Want to talk to someone first? [Join us on Discord](https://discord.gg/GS9js2XkJR) — we're +happy to help you find something to work on. - +## Where to start -- [Contributing to Code Carbon](#contributing-to-code-carbon) - - [Have a Question?](#have-a-question) - - [Found a Bug?](#found-a-bug) - - [Have a Feature Request?](#have-a-feature-request) - - [Alternative ways of contributing](#alternative-ways-of-contributing) - - [Ready to Contribute!](#ready-to-contribute) - - [Installation](#installation) - - [Some UV commands](#some-uv-commands) - - [Tests](#tests) - - [Stress your computer](#stress-your-computer) - - [Update all dependencies](#update-all-dependencies) - - [Branching and Pull Requests](#branching-and-pull-requests) - - [Debug in VS Code](#debug-in-vs-code) - - [Coding style \&\& Linting](#coding-style-linting) - - [Dependencies management](#dependencies-management) - - [Build Documentation 🖨️](#build-documentation) - - [Release process](#release-process) - - [Test the build in Docker](#test-the-build-in-docker) - - [API and Dashboard](#api-and-dashboard) - - [CSV Dashboard](#csv-dashboard) - - [Web dashboard](#web-dashboard) - - [API](#api) - - [Test the API](#test-the-api) - - [Restore database from a production Backup](#restore-database-from-a-production-backup) - - [Deployment](#deployment) - - [API](#api-1) - - [Dashboard](#dashboard) - - [License](#license) +- **Have a question?** Check the [FAQ](https://docs.codecarbon.io/latest/explanation/faq/), + then ask on [Discord](https://discord.gg/GS9js2XkJR). +- **Found a bug?** [Open an issue](https://github.com/mlco2/codecarbon/issues/new), and feel + free to send a pull request with the fix too. +- **Have a feature request?** + [Open an issue](https://github.com/mlco2/codecarbon/issues/new) describing the feature and + its intent. Please search the existing issues first to avoid duplicates. +- **Want to write code?** Look for + [good first issues](https://github.com/mlco2/codecarbon/labels/good%20first%20issue) and + [help wanted](https://github.com/mlco2/codecarbon/labels/help%20wanted), or pick something + from the [prioritized board](https://github.com/orgs/mlco2/projects/1). +- **Your CPU isn't recognised?** Adding it to `codecarbon/data/hardware/cpu_power.csv` is a + genuinely useful first contribution: it improves accuracy for everyone with that hardware. +- **Documentation unclear?** Every docs page has an edit button. - +## Set up your environment - - -## Have a Question? - -Please see the [FAQ](https://docs.codecarbon.io/latest/explanation/faq/) for questions. You can also ask on our [Discord community](https://discord.gg/GS9js2XkJR) – we're happy to help! - - - -## Found a Bug? - -If you've identified a bug in `codecarbon`, please [submit an issue](https://github.com/mlco2/codecarbon/issues/new) to the GitHub repo: [mlco2/codecarbon](https://github.com/mlco2/codecarbon/issues/new). Please also feel free to submit a PR with a fix for the bug! - - - -## Have a Feature Request? - -Feel free to describe your request by [submitting an issue](https://github.com/mlco2/codecarbon/issues/new) documenting the feature (with its intent) and a PR with a proposed implementation of the feature. - -Before submitting a new issue, please search the issues to make sure there isn't a similar issue already. -New issues can be created within the [GitHub repo](https://github.com/mlco2/codecarbon/issues/new). - - -## Alternative ways of contributing - -You have a cool idea, but do not know if it fits with Code Carbon? You can create an issue to share: - -- the code, via the Github repo or [Binder](https://mybinder.org/), to share executable notebooks -- a webapp, using [Voilà](https://github.com/voila-dashboards/voila), [Dash](https://github.com/plotly/dash) or [Streamlit](https://github.com/streamlit/streamlit) -- ideas for improvement about the tool or its documentation - - -## Ready to Contribute! - - - -### Installation - -CodeCarbon is a Python package, to contribute to it, you need to have Python installed on your machine, natively or with [UV](https://github.com/astral-sh/uv). - -Between April 2024 and July 2025 we used Hatch for managing development environment. Since August 2025 we use UV to manage the environments, Python versions, and dependencies - it's a fast, reliable way to work with Python projects. - -We have dropped support of Python 3.6 since version 2.0.0 of CodeCarbon. - -We have dropped support of Python 3.8 and 3.9 since version 3.2.4 of CodeCarbon. - -Please install [UV](https://github.com/astral-sh/uv) following [installation instructions](https://docs.astral.sh/uv/getting-started/installation/#standalone-installer). - -Then, clone the repository and create the environment with: +CodeCarbon is a Python package. We use [UV](https://github.com/astral-sh/uv) to manage +environments, Python versions and dependencies — install it with the +[standalone installer](https://docs.astral.sh/uv/getting-started/installation/#standalone-installer), +then: ```sh git clone https://github.com/mlco2/codecarbon.git @@ -91,490 +35,61 @@ uv sync uv run task pre-commit-install ``` - -### Some UV commands - -UV simplifies Python package management with fast, reliable commands: - -```sh -# Show dependencies -uv tree -# Add a default dependency -uv add pandas -# Add a dev dependency -uv add --dev pytest -# Add a dependency for an extra feature -uv add --optional api logfire[fastapi] -# List all task for CodeCarbon -uv run task -l -# Run a specific version of python -uv run --python 3.14 codecarbon monitor -``` - - -### Tests - -You can run the unit tests by running UV in the terminal when in the root package directory: +## Run the tests ```sh uv run task test-package ``` -Run a specific test file: - -```sh -uv run python -m pytest tests/test_cpu.py -``` - -You can also run a specific test: - -```sh -uv run python -m unittest tests.test_your_feature.YourTestCase.test_function -``` - -For example: `uv run python -m unittest tests.test_energy.TestEnergy.test_wraparound_delta_correct_value` - -Some tests will fail if you do not set *CODECARBON_ALLOW_MULTIPLE_RUNS* with `export CODECARBON_ALLOW_MULTIPLE_RUNS=True` before running test manually. - -To test the API, see [how to run it locally](#api) first. - -Core and external classes are unit tested, with one test file per class. Most pull requests are expected to contain either new tests or test updates. If you are unusure what to test / how to test it, please put it in the pull request description and the maintainers will help you. - - - -### Stress your computer - -To test CodeCarbon, it is useful to stress your computer to make it use its full power: - -- 7Zip is often already installed, running it with `7z b` makes a quick CPU test. -- [GPU-burn](https://github.com/wilicc/gpu-burn) will load test the GPU for a configurable duration. -- To test the CPU : `stress-ng --cpu 0 --cpu-method matrixprod --metrics-brief --rapl --perf -t 60s` See [our documentation](https://docs.codecarbon.io/latest/how-to/test-on-scaleway/) to install it. -- To do useful computation while testing [Folding At Home](https://foldingathome.org/) is a good option. -- [OCCT](https://www.ocbase.com/download) is a proprietary tool but free for non-commercial use and available for Windows and Linux. - -To monitor the power consumption of your computer while stressing it, you can use: - -- `nvidia-smi` is a useful tool to see the metrics of the GPU and compare it with CodeCarbon. -- [powerstat](https://github.com/ColinIanKing/powerstat) can be used to see the metrics of the CPU and compare it with CodeCarbon. It's available on major distribution, like Debian-based Linux distributions with `sudo apt install powerstat`. Run it with `sudo powerstat -a -R 1 60`. - - - -### Update all dependencies - -For multiple requirement files: -```sh -uv sync --upgrade -``` - - -### Branching and Pull Requests - -To add a new feature to codecarbon, apply the following workflow: - -- Master branch is protected -- To contribute to an already [prioritized](https://github.com/orgs/mlco2/projects/1) feature, you can create a branch from master and open a draft PR -- Documenting the intent & the limits of a contribution in a dedicated issue or in the pull request helps the review -- Once automated tests pass, the PR is reviewed and merged by the repository maintainers - - - -### Debug in VS Code - -Here is the launch.json to be able to debug examples and tests: - -```json -{ - "version": "0.2.0", - "configurations": [ - - { - "name": "Python: Current File", - "type": "debugpy", - "request": "launch", - "program": "${file}", - "console": "integratedTerminal", - "justMyCode": true, - "env": { "PYTHONPATH": "${workspaceRoot}" } - }, - { - "name": "PyTest: Current File", - "type": "debugpy", - "request": "launch", - "module": "pytest", - "args": [ - "-s", - "${file}" - ], - "console": "integratedTerminal", - "justMyCode": true, - "env": { "PYTHONPATH": "${workspaceRoot}", - "CODECARBON_ALLOW_MULTIPLE_RUNS": "True" } - }, - { - "name": "PyTest: codecarbon monitor", - "type": "debugpy", - "request": "launch", - "module": "codecarbon.cli.main", - "args": [ - "monitor" - ], - "console": "integratedTerminal", - "justMyCode": true, - "env": { "PYTHONPATH": "${workspaceRoot}"} - } - ] -} -``` - -Then run opened test with this button: - -![vscode_debug](../images/vscode_debug.png) - - - -### Coding style && Linting - -The coding style and linting rules are automatically applied and enforced by [pre-commit](https://pre-commit.com/). This tool helps to maintain the same code style across the code-base such to ease the review and collaboration process. Once installed ([https://pre-commit.com/#installation](https://pre-commit.com/#installation)), you can install a Git hook to automatically run pre-commit (and all configured linters/auto-formatters) before doing a commit with `uv run task precommit-install`. Then once you tried to commit, the linters/formatters will run automatically. It should display something similar to: - -```log -[INFO] Initializing environment for https://github.com/psf/black. -[INFO] Initializing environment for https://gitlab.com/pycqa/flake8. -[INFO] Installing environment for https://github.com/psf/black. -[INFO] Once installed this environment will be reused. -[INFO] This may take a few minutes... -[INFO] Installing environment for https://gitlab.com/pycqa/flake8. -[INFO] Once installed this environment will be reused. -[INFO] This may take a few minutes... -seed isort known_third_party.............................................Passed -isort....................................................................Failed -- hook id: isort -- files were modified by this hook - -Fixing codecarbon/__init__.py - -black....................................................................Passed -flake8...................................................................Passed -``` - -If any of the linters/formatters fail, check the difference with `git diff`, add the differences if there is no behavior changes (isort and black might have change some coding style or import order, this is expected it is their job) with `git add` and finally try to commit again `git commit ...`. - -You can also run `pre-commit` with `uv run pre-commit run --all-file` to check all file. - - - -### Dependencies management - -Dependencies are defined in different places: - -- In [pyproject.toml](https://github.com/mlco2/codecarbon/blob/master/pyproject.toml#L28), those are all the dependencies. -- In [uv.lock](https://github.com/mlco2/codecarbon/blob/master/uv.lock), those are the locked dependencies managed by UV, do not edit them. - - - -### Build Documentation 🖨️ - -No software is complete without great documentation! -To make generating documentation easier, we use [Zensical](https://zensical.org/). - -In order to make changes, edit the `.md` files in the `/docs` folder, and then run in root folder: - -```sh -uv run --only-group doc task docs -``` - -to regenerate the html files. For local preview with live reload, run `uv run --only-group doc task docs-serve`. - -### Rebase your branch on master - -Before creating a PR, please make sure to rebase your branch on master to avoid merge conflicts and make the review easier. You can do it with the following command: -```sh -# Be careful, this command will delete every local changes you have, make sure to commit or stash them before running it -TARGET_BRANCH=master -current_branch=$(git symbolic-ref --short HEAD) -git switch $TARGET_BRANCH && git pull -git switch $current_branch --force && git fetch origin $TARGET_BRANCH -git rebase $TARGET_BRANCH -``` - -In case of a conflict during a rebase, "incoming" refers to your branch, and "current" refers to master. This is because the commits from your branch are being applied to master, so they are incoming. In case of a merge, it's the opposite! - -Check if everything is fine: - -```sh -git status -``` - -Push force -```sh -git push --force-with-lease -``` - - -### Release process - -- Merge all PRs. -- Open a terminal and make sure you are not in a venv with `deactivate`. -- Create a PR bumping the version with `uv run bumpver update --patch`. For a release candidate, use `uv run bumpver update --set-version 3.0.0_rc1`. -- Run `uv run python .github/pyproject_versions.py -c` to check version consistancy. -- Update the dependencies with `uv sync --upgrade` -- [Build Documentation](#build-documentation) with `uv run --only-group doc task docs`. -- Push the changes. -- Merge the PR. -- Wait for the Github Action `ReleaseDrafter` to finish running on the merge commit. -- [Edit the Draft release](https://github.com/mlco2/codecarbon/releases/) on Github and give it a tag, `v1.0.0` for the version 1.0.0. Github will automatically create a Git tag for it. Complete help [here](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository). -- A [Github Action](https://github.com/mlco2/codecarbon/actions) _Upload Python Package_ will be run automaticaly to upload the package. - -#### Test the release - -After the release on PyPi, please test it in a fresh environment: - -```sh -cd /tmp -rm -rf cc_rel_test -python -m venv cc_rel_test -source cc_rel_test/bin/activate -pip install codecarbon -# Check you have the last version -codecarbon --version -codecarbon monitor --offline --country-iso-code FRA -# Stop it with Ctrl+C if it works -# Then clean up -rm -rf cc_rel_test -``` - -And check if the doc looks good on [docs.codecarbon.io](https://docs.codecarbon.io/). - - -#### Test the build in Docker - -If you want to check the build is working, you could run: - -```bash -rm dist/* -uv build -docker run -it --rm -v $PWD:/data python:3.13 /bin/bash -pip install pytest pytest-mock requests-mock responses pandas -pip install --no-cache-dir /data/dist/codecarbon-*.whl -U --force-reinstall -cp /data/tests/test_package_integrity.py . -pytest test_package_integrity.py -``` - -### Contribute to a fork branch - -When a user open a PR from a fork, we are allowed to push to the fork branch. - -If you want to do so, do the following: - -```bash -git remote add https://github.com//codecarbon.git -git fetch -git checkout -b / -``` - - -## API and Dashboard - - -### CSV Dashboard - -To run locally the dashboard application, you can use it out on a sample data file such as the one in `examples/emissions.csv`, and run it with the following command from the code base: - -```bash -uv run --extra carbonboard task carbonboard --filepath="examples/emissions.csv" - -# or, if you don't want to use UV -pip install codecarbon[carbonboard] -python codecarbon/viz/carbonboard.py --filepath="examples/emissions.csv" -``` - -> **Note:** The `viz-legacy` extra is deprecated but still works for backwards compatibility. It will be removed in v4.0.0. Please use `carbonboard` instead. - -If you have the package installed, you can run the CLI command: - -```bash -carbonboard --filepath="examples/emissions.csv" --port=8050 -``` - - -### Web dashboard - -To test the new dashboard that uses the API, run: - -```sh -uv run task local -``` - -Then, click on the url displayed in the terminal. - -By default, the dashboard is connected to the production API, to connect it to your local API, you can set the environment variable `CODECARBON_API_URL` to `http://localhost:8008` : - -```sh -export CODECARBON_API_URL=http://localhost:8008 -uv run dashboard -``` - - - -### API - -The easiest way to run the API locally is with Docker, it will set-up the Postgres database for you. Launch this command in the project directory: - -```sh -uv run api.docker - -# or - -docker-compose up -d -``` - -Please see [Docker specific documentation](https://github.com/mlco2/codecarbon/blob/master/docker/README.md) for more informations. -When up, the API documentation is available locally at the following URL: http://localhost:8008/redoc and can be used for testing. - -If you want to run the API without Docker, you must first set the environment variables described in the .env.example file, and run the following command: - -```sh -uv run api.local -``` - -In order to make codecarbon automatically connect to the local API, create a file `.codecarbon.config` with contents: - -``` -[codecarbon] -api_endpoint = http://localhost:8008 -``` - -Before using it, you need an experiment_id, to get one, run: - -``` -codecarbon login -``` - -It will ask the API for an experiment_id on the default project and save it to `.codecarbon.config` for you. - -Then you could run an example: - -``` -python examples/api_call_debug.py -``` - -📝 Edit the line `occurence = 60 * 24 * 365 * 100` to specify the number of minutes you want to run it. - - - -### Test the API - -Test dependencies (pytest, pytest-asyncio, etc.) are in the `dev` optional group. Install them first: - -```sh -uv sync --project carbonserver --extra dev -``` - -Then run: - -```sh -uv run task test-api-unit -``` - -```sh -export CODECARBON_API_URL=http://localhost:8008 -uv run task test-api-integ -``` - - -### Restore database from a production Backup - -```sh -docker cp postgresql_*.dump postgres_codecarbon:/tmp -docker exec -it postgres_codecarbon bash -export BACKUP_USER=upwnpbdktjvnoks0foxq -export BACKUP_DB=bnrwiktgr4hzukt1xseg -psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE USER $BACKUP_USER WITH PASSWORD '$POSTGRES_PASSWORD';" -psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER USER $BACKUP_USER CREATEDB;" -createdb -U $BACKUP_USER $BACKUP_DB -psql -U $BACKUP_USER -d $POSTGRES_DB -c "CREATE DATABASE $BACKUP_DB;" -pg_restore -d $BACKUP_DB -U $BACKUP_USER --jobs=8 --clean --create /tmp/postgresql_*.dump -psql -U $BACKUP_USER -d $BACKUP_DB -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"$POSTGRES_USER\";" -psql -U $POSTGRES_USER -d $BACKUP_DB -c "ALTER DATABASE $POSTGRES_DB RENAME TO \"$POSTGRES_DB-backup\";" -psql -U $BACKUP_USER -d $POSTGRES_DB-backup -c "ALTER DATABASE $BACKUP_DB RENAME TO $POSTGRES_DB;" -``` - -#### Clean the database - -To remove orphans (elements without run) from the database, run: - -```sql -CALL public.spcc_purgeduplicatedata(); -``` - +Some tests fail unless you set `export CODECARBON_ALLOW_MULTIPLE_RUNS=True` before running +them manually. Core and external classes are unit tested, with one test file per class. Most +pull requests are expected to contain either new tests or test updates. If you are unsure +what to test or how to test it, say so in the pull request description and the maintainers +will help you. - -### Deployment - - - -#### API - -The API is available to everyone from https://api.codecarbon.io, but if you want to deploy it for yourself, here are the instructions. - -To deploy the API we use [Clever Cloud](https://www.clever-cloud.com/), an IT Automation platform. They manage all the hard ops work while we focus on the Code Carbon value. - -Here is the Clever Cloud configuration if you want to reproduce it: - -```conf -APP_FOLDER="carbonserver" -CC_PIP_REQUIREMENTS_FILE="requirements.txt" -CC_POST_BUILD_HOOK="cd $APP_HOME/carbonserver && python3 -m alembic -c carbonserver/database/alembic.ini upgrade head" -CC_PYTHON_BACKEND="uvicorn" -CC_PYTHON_MODULE="main:app" -CC_PYTHON_VERSION="3.13" -DATABASE_URL="postgresql://secret_do_not_publish_this" -PORT="8080" -``` - -_CC stand here for Clever Cloud, not Code Carbon_ 😉 - -To deploy, +## Check style before you push ```sh -git remote add deploy git+ssh://git@push-n2-par-clevercloud-customers.services.clever-cloud.com/app_.git -git push deploy master:master +uv run task format +uv run task lint ``` -Yeah, not so hard, is it? +The pre-commit hook runs these for you. -See [the doc](https://www.clever-cloud.com/doc/getting-started/quickstart/) for more informations. +## Open your pull request -Please note that Clever Cloud host Code Carbon for free because they like our project. +`master` is protected, so branch from it and open a pull request — draft pull requests are +welcome if you want early feedback. Keep the change focused and describe the problem it +solves; documenting the intent and the limits of a contribution, in the pull request or in a +dedicated issue, helps the review. Once the automated tests pass, a maintainer reviews and +merges it. +## Alternative ways of contributing - -#### Dashboard - -Same as for the API, for example to deploy the branch `fix-unit` to CleverCloud: - -```sh -git push clever-dashboard fix-unit:master -``` +You have a cool idea, but do not know if it fits with CodeCarbon? You can create an issue to +share: -Config on CleverCloud: +- the code, via the GitHub repo or [Binder](https://mybinder.org/), to share executable notebooks +- a webapp, using [Voilà](https://github.com/voila-dashboards/voila), [Dash](https://github.com/plotly/dash) or [Streamlit](https://github.com/streamlit/streamlit) +- ideas for improvement about the tool or its documentation -```sh -APP_FOLDER="dashboard" -CC_PIP_REQUIREMENTS_FILE="requirements-dashboard.txt" -CC_PYTHON_MODULE="carbon_board_API:server" -CC_PYTHON_VERSION="3.13" -CODECARBON_API_URL="https://api.codecarbon.io" -PORT="8000" -``` +## More detail +- [Development guide](development.md) — UV commands, debugging, stress testing, dependency + management, building the docs, running the API and dashboards locally +- [Maintainer guide](../maintaining.md) — release process and deployment +- [AI policy](ai_policy.md) +- [Code of conduct](code-of-conduct.md) ## Questions or Need Help? -Got stuck? Have an idea? Want to share your contribution? **[Join us on Discord](https://discord.gg/GS9js2XkJR)** – our community is here to help and support you! +Got stuck? Have an idea? Want to share your contribution? +**[Join us on Discord](https://discord.gg/GS9js2XkJR)** – our community is here to help and +support you! - ## License -By contributing your code, you agree to license your contribution under the terms of the [MIT License](https://github.com/mlco2/codecarbon/blob/master/LICENSE). +By contributing your code, you agree to license your contribution under the terms of the +[MIT License](https://github.com/mlco2/codecarbon/blob/master/LICENSE). All files are released with the MIT license. diff --git a/docs/how-to/development.md b/docs/how-to/development.md new file mode 100644 index 000000000..87788ed88 --- /dev/null +++ b/docs/how-to/development.md @@ -0,0 +1,325 @@ +# Development guide + +This page holds the deeper development material. If this is your first contribution, start +with the [contributing guide](contributing.md). + +## Python versions + +Between April 2024 and July 2025 we used Hatch for managing the development environment. +Since August 2025 we use UV to manage the environments, Python versions, and dependencies — +it's a fast, reliable way to work with Python projects. + +We have dropped support of Python 3.6 since version 2.0.0 of CodeCarbon. + +We have dropped support of Python 3.8 and 3.9 since version 3.2.4 of CodeCarbon. + +## Some UV commands + +UV simplifies Python package management with fast, reliable commands: + +```sh +# Show dependencies +uv tree +# Add a default dependency +uv add pandas +# Add a dev dependency +uv add --dev pytest +# Add a dependency for an extra feature +uv add --optional api logfire[fastapi] +# List all task for CodeCarbon +uv run task -l +# Run a specific version of python +uv run --python 3.14 codecarbon monitor +``` + +## Tests + +You can run the unit tests by running UV in the terminal when in the root package directory: + +```sh +uv run task test-package +``` + +Run a specific test file: + +```sh +uv run python -m pytest tests/test_cpu.py +``` + +You can also run a specific test: + +```sh +uv run python -m unittest tests.test_your_feature.YourTestCase.test_function +``` + +For example: `uv run python -m unittest tests.test_energy.TestEnergy.test_wraparound_delta_correct_value` + +Some tests will fail if you do not set *CODECARBON_ALLOW_MULTIPLE_RUNS* with `export CODECARBON_ALLOW_MULTIPLE_RUNS=True` before running test manually. + +To test the API, see [how to run it locally](#api) first. + +Core and external classes are unit tested, with one test file per class. Most pull requests are expected to contain either new tests or test updates. If you are unusure what to test / how to test it, please put it in the pull request description and the maintainers will help you. + +## Stress your computer + +To test CodeCarbon, it is useful to stress your computer to make it use its full power: + +- 7Zip is often already installed, running it with `7z b` makes a quick CPU test. +- [GPU-burn](https://github.com/wilicc/gpu-burn) will load test the GPU for a configurable duration. +- To test the CPU : `stress-ng --cpu 0 --cpu-method matrixprod --metrics-brief --rapl --perf -t 60s` See [our documentation](https://docs.codecarbon.io/latest/how-to/test-on-scaleway/) to install it. +- To do useful computation while testing [Folding At Home](https://foldingathome.org/) is a good option. +- [OCCT](https://www.ocbase.com/download) is a proprietary tool but free for non-commercial use and available for Windows and Linux. + +To monitor the power consumption of your computer while stressing it, you can use: + +- `nvidia-smi` is a useful tool to see the metrics of the GPU and compare it with CodeCarbon. +- [powerstat](https://github.com/ColinIanKing/powerstat) can be used to see the metrics of the CPU and compare it with CodeCarbon. It's available on major distribution, like Debian-based Linux distributions with `sudo apt install powerstat`. Run it with `sudo powerstat -a -R 1 60`. + +## Update all dependencies + +For multiple requirement files: +```sh +uv sync --upgrade +``` + +## Debug in VS Code + +Here is the launch.json to be able to debug examples and tests: + +```json +{ + "version": "0.2.0", + "configurations": [ + + { + "name": "Python: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": true, + "env": { "PYTHONPATH": "${workspaceRoot}" } + }, + { + "name": "PyTest: Current File", + "type": "debugpy", + "request": "launch", + "module": "pytest", + "args": [ + "-s", + "${file}" + ], + "console": "integratedTerminal", + "justMyCode": true, + "env": { "PYTHONPATH": "${workspaceRoot}", + "CODECARBON_ALLOW_MULTIPLE_RUNS": "True" } + }, + { + "name": "PyTest: codecarbon monitor", + "type": "debugpy", + "request": "launch", + "module": "codecarbon.cli.main", + "args": [ + "monitor" + ], + "console": "integratedTerminal", + "justMyCode": true, + "env": { "PYTHONPATH": "${workspaceRoot}"} + } + ] +} +``` + +Then run opened test with this button: + +![vscode_debug](../images/vscode_debug.png) + +## Coding style && Linting + +The coding style and linting rules are automatically applied and enforced by [pre-commit](https://pre-commit.com/). This tool helps to maintain the same code style across the code-base such to ease the review and collaboration process. Once installed ([https://pre-commit.com/#installation](https://pre-commit.com/#installation)), you can install a Git hook to automatically run pre-commit (and all configured linters/auto-formatters) before doing a commit with `uv run task precommit-install`. Then once you tried to commit, the linters/formatters will run automatically. It should display something similar to: + +```log +[INFO] Initializing environment for https://github.com/psf/black. +[INFO] Initializing environment for https://gitlab.com/pycqa/flake8. +[INFO] Installing environment for https://github.com/psf/black. +[INFO] Once installed this environment will be reused. +[INFO] This may take a few minutes... +[INFO] Installing environment for https://gitlab.com/pycqa/flake8. +[INFO] Once installed this environment will be reused. +[INFO] This may take a few minutes... +seed isort known_third_party.............................................Passed +isort....................................................................Failed +- hook id: isort +- files were modified by this hook + +Fixing codecarbon/__init__.py + +black....................................................................Passed +flake8...................................................................Passed +``` + +If any of the linters/formatters fail, check the difference with `git diff`, add the differences if there is no behavior changes (isort and black might have change some coding style or import order, this is expected it is their job) with `git add` and finally try to commit again `git commit ...`. + +You can also run `pre-commit` with `uv run pre-commit run --all-file` to check all file. + +## Dependencies management + +Dependencies are defined in different places: + +- In [pyproject.toml](https://github.com/mlco2/codecarbon/blob/master/pyproject.toml#L28), those are all the dependencies. +- In [uv.lock](https://github.com/mlco2/codecarbon/blob/master/uv.lock), those are the locked dependencies managed by UV, do not edit them. + +## Build Documentation 🖨️ + +No software is complete without great documentation! +To make generating documentation easier, we use [Zensical](https://zensical.org/). + +In order to make changes, edit the `.md` files in the `/docs` folder, and then run in root folder: + +```sh +uv run --only-group doc task docs +``` + +to regenerate the html files. For local preview with live reload, run `uv run --only-group doc task docs-serve`. + +## Rebase your branch on master + +Before creating a PR, please make sure to rebase your branch on master to avoid merge conflicts and make the review easier. You can do it with the following command: +```sh +# Be careful, this command will delete every local changes you have, make sure to commit or stash them before running it +TARGET_BRANCH=master +current_branch=$(git symbolic-ref --short HEAD) +git switch $TARGET_BRANCH && git pull +git switch $current_branch --force && git fetch origin $TARGET_BRANCH +git rebase $TARGET_BRANCH +``` + +In case of a conflict during a rebase, "incoming" refers to your branch, and "current" refers to master. This is because the commits from your branch are being applied to master, so they are incoming. In case of a merge, it's the opposite! + +Check if everything is fine: + +```sh +git status +``` + +Push force +```sh +git push --force-with-lease +``` + +## Contribute to a fork branch + +When a user open a PR from a fork, we are allowed to push to the fork branch. + +If you want to do so, do the following: + +```bash +git remote add https://github.com//codecarbon.git +git fetch +git checkout -b / +``` + +## API and Dashboard + +### CSV Dashboard + +To run locally the dashboard application, you can use it out on a sample data file such as the one in `examples/emissions.csv`, and run it with the following command from the code base: + +```bash +uv run --extra carbonboard task carbonboard --filepath="examples/emissions.csv" + +# or, if you don't want to use UV +pip install codecarbon[carbonboard] +python codecarbon/viz/carbonboard.py --filepath="examples/emissions.csv" +``` + +> **Note:** The `viz-legacy` extra is deprecated but still works for backwards compatibility. It will be removed in v4.0.0. Please use `carbonboard` instead. + +If you have the package installed, you can run the CLI command: + +```bash +carbonboard --filepath="examples/emissions.csv" --port=8050 +``` + +### Web dashboard + +To test the new dashboard that uses the API, run: + +```sh +uv run task local +``` + +Then, click on the url displayed in the terminal. + +By default, the dashboard is connected to the production API, to connect it to your local API, you can set the environment variable `CODECARBON_API_URL` to `http://localhost:8008` : + +```sh +export CODECARBON_API_URL=http://localhost:8008 +uv run dashboard +``` + +### API + +The easiest way to run the API locally is with Docker, it will set-up the Postgres database for you. Launch this command in the project directory: + +```sh +uv run api.docker + +# or + +docker-compose up -d +``` + +Please see [Docker specific documentation](https://github.com/mlco2/codecarbon/blob/master/docker/README.md) for more informations. +When up, the API documentation is available locally at the following URL: http://localhost:8008/redoc and can be used for testing. + +If you want to run the API without Docker, you must first set the environment variables described in the .env.example file, and run the following command: + +```sh +uv run api.local +``` + +In order to make codecarbon automatically connect to the local API, create a file `.codecarbon.config` with contents: + +``` +[codecarbon] +api_endpoint = http://localhost:8008 +``` + +Before using it, you need an experiment_id, to get one, run: + +``` +codecarbon login +``` + +It will ask the API for an experiment_id on the default project and save it to `.codecarbon.config` for you. + +Then you could run an example: + +``` +python examples/api_call_debug.py +``` + +📝 Edit the line `occurence = 60 * 24 * 365 * 100` to specify the number of minutes you want to run it. + +### Test the API + +Test dependencies (pytest, pytest-asyncio, etc.) are in the `dev` optional group. Install them first: + +```sh +uv sync --project carbonserver --extra dev +``` + +Then run: + +```sh +uv run task test-api-unit +``` + +```sh +export CODECARBON_API_URL=http://localhost:8008 +uv run task test-api-integ +``` + +Database restore, deployment and the release process live in the +[maintainer guide](../maintaining.md). diff --git a/docs/how-to/enable-rapl.md b/docs/how-to/enable-rapl.md index ce297380b..ea0f0c9db 100644 --- a/docs/how-to/enable-rapl.md +++ b/docs/how-to/enable-rapl.md @@ -8,6 +8,8 @@ Without RAPL, CodeCarbon estimates CPU power based on hardware specifications an - ✅ **Direct hardware measurements** — Read CPU energy directly from RAPL counters - ✅ **Higher precision** — Microjoule-level accuracy instead of estimates + +On the CPUs we profiled, the load-and-TDP estimate deviated from RAPL by up to roughly a factor of two in either direction. See [Accuracy and validation](../explanation/accuracy.md) for the measured figures. - ✅ **Multi-domain support** — Measure package, core, uncore, DRAM, and GPU separately - ✅ **Real-time data** — No delay or aggregation artifacts diff --git a/docs/how-to/examples.md b/docs/how-to/examples.md index 06f084aca..7380c5933 100644 --- a/docs/how-to/examples.md +++ b/docs/how-to/examples.md @@ -1,97 +1,12 @@ # CodeCarbon Examples -The directory [examples/](https://github.com/mlco2/codecarbon/tree/master/examples) contains practical examples demonstrating how to use CodeCarbon to track carbon emissions from your computing tasks. The examples below are organized by use case rather than alphabetically. - -## Quick Start Examples - -| Example | Type | Description | -|---------|------|-------------| -| [print_hardware.py](https://github.com/mlco2/codecarbon/blob/master/examples/print_hardware.py) | Python Script | Detect and display available hardware (CPU, GPU, RAM) on your system | -| [command_line_tool.py](https://github.com/mlco2/codecarbon/blob/master/examples/command_line_tool.py) | Python Script | Track emissions of external command-line tools executed via subprocess | - -## Tracking Methods - -| Example | Type | Description | -|---------|------|-------------| -| [mnist_decorator.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist_decorator.py) | Python Script | Track emissions using the `@track_emissions` decorator on functions | -| [mnist_context_manager.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist_context_manager.py) | Python Script | Track emissions using `EmissionsTracker` as a context manager (with statement) | -| [mnist_callback.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist_callback.py) | Python Script | Track emissions using Keras/TensorFlow callbacks during model training | -| [api_call_demo.py](https://github.com/mlco2/codecarbon/blob/master/examples/api_call_demo.py) | Python Script | Track emissions and send data to the CodeCarbon API with `@track_emissions` | - -## Basic Model Training - -| Example | Type | Description | -|---------|------|-------------| -| [mnist.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist.py) | Python Script | Train a simple neural network on MNIST dataset with TensorFlow | -| [mnist-sklearn.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist-sklearn.py) | Python Script | Train a scikit-learn model on MNIST and track emissions | -| [pytorch-multigpu-example.py](https://github.com/mlco2/codecarbon/blob/master/examples/pytorch-multigpu-example.py) | Python Script | PyTorch CNN training on MNIST with multi-GPU support | - -## Hyperparameter Search - -| Example | Type | Description | -|---------|------|-------------| -| [mnist_grid_search.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist_grid_search.py) | Python Script | Grid search hyperparameter optimization with emission tracking | -| [mnist_random_search.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist_random_search.py) | Python Script | Random search hyperparameter optimization with emission tracking | - -## ML Model Inference - -| Example | Type | Description | -|---------|------|-------------| -| [bert_inference.py](https://github.com/mlco2/codecarbon/blob/master/examples/bert_inference.py) | Python Script | BERT language model inference with task-level tracking | -| [task_inference.py](https://github.com/mlco2/codecarbon/blob/master/examples/task_inference.py) | Python Script | Track emissions for different inference tasks (load dataset, build model, predict) | -| [task_loop_same_task.py](https://github.com/mlco2/codecarbon/blob/master/examples/task_loop_same_task.py) | Python Script | Track emissions running the same task multiple times | -| [transformers_smollm2.py](https://github.com/mlco2/codecarbon/blob/master/examples/transformers_smollm2.py) | Python Script | Small language model (SmolLM2) inference from Hugging Face | -| [ollama_local_api.py](https://github.com/mlco2/codecarbon/blob/master/examples/ollama_local_api.py) | Python Script | Track emissions of local LLM API calls using Ollama | - -## Hardware-Specific Examples - -| Example | Type | Description | -|---------|------|-------------| -| [intel_npu.py](https://github.com/mlco2/codecarbon/blob/master/examples/intel_npu.py) | Python Script | Intel Neural Processing Unit (NPU) support for model inference | -| [full_cpu.py](https://github.com/mlco2/codecarbon/blob/master/examples/full_cpu.py) | Python Script | Demonstrate full CPU utilization and emission tracking | - -## Parallel & Concurrent Processing - -| Example | Type | Description | -|---------|------|-------------| -| [multithread.py](https://github.com/mlco2/codecarbon/blob/master/examples/multithread.py) | Python Script | Track emissions from multithreaded workloads | -| [compare_cpu_load_and_RAPL.py](https://github.com/mlco2/codecarbon/blob/master/examples/compare_cpu_load_and_RAPL.py) | Python Script | Compare RAPL power measurement vs CPU load estimation in parallel workloads | - -## Logging & Output Integration - -| Example | Type | Description | -|---------|------|-------------| -| [boamps_output.py](https://github.com/mlco2/codecarbon/blob/master/examples/boamps_output.py) | Python Script | Write the output in [BoAmps](https://github.com/Boavizta/BoAmps) format. | -| [logging_to_file.py](https://github.com/mlco2/codecarbon/blob/master/examples/logging_to_file.py) | Python Script | Save emissions data to a local CSV file | -| [logging_to_file_exclusive_run.py](https://github.com/mlco2/codecarbon/blob/master/examples/logging_to_file_exclusive_run.py) | Python Script | Long-running process with exclusive file logging | -| [logging_to_google_cloud.py](https://github.com/mlco2/codecarbon/blob/master/examples/logging_to_google_cloud.py) | Python Script | Send emissions data to Google Cloud Logging | -| [logfire_metrics.py](https://github.com/mlco2/codecarbon/blob/master/examples/logfire_metrics.py) | Python Script | Integrate CodeCarbon with Logfire metrics platform | -| [prometheus_call.py](https://github.com/mlco2/codecarbon/blob/master/examples/prometheus_call.py) | Python Script | Export emissions metrics to Prometheus | -| [mnist-comet.py](https://github.com/mlco2/codecarbon/blob/master/examples/mnist-comet.py) | Python Script | Integrate emission tracking with Comet.ml experiment tracking | - -## Metrics & Analysis - -| Example | Type | Description | -|---------|------|-------------| -| [pue.py](https://github.com/mlco2/codecarbon/blob/master/examples/pue.py) | Python Script | Calculate Power Usage Effectiveness (PUE) with CodeCarbon | -| [wue.py](https://github.com/mlco2/codecarbon/blob/master/examples/wue.py) | Python Script | Calculate Water Usage Effectiveness (WUE) of your computing | - -## Interactive Notebooks - -| Example | Type | Description | -|---------|------|-------------| -| [notebook.ipynb](https://github.com/mlco2/codecarbon/blob/master/examples/notebook.ipynb) | Jupyter Notebook | Basic CodeCarbon usage in Jupyter environment | -| [compare_cpu_load_and_RAPL.ipynb](https://github.com/mlco2/codecarbon/blob/master/examples/compare_cpu_load_and_RAPL.ipynb) | Jupyter Notebook | Compare different power measurement methods (RAPL vs CPU load) | -| [local_llms.ipynb](https://github.com/mlco2/codecarbon/blob/master/examples/local_llms.ipynb) | Jupyter Notebook | Track emissions of local LLM inference | - -## Setup & Configuration - -| Item | Description | -|------|-------------| -| [requirements-examples.txt](https://github.com/mlco2/codecarbon/blob/master/examples/requirements-examples.txt) | Python dependencies for running the examples | -| [rapl/](https://github.com/mlco2/codecarbon/blob/master/examples/rapl/) | Setup instructions for RAPL power measurement support | -| [slurm_rocm/](https://github.com/mlco2/codecarbon/blob/master/examples/slurm_rocm/) | Configuration for SLURM job scheduler with ROCm GPU support | -| [notebooks/](https://github.com/mlco2/codecarbon/blob/master/examples/notebooks/) | Additional Jupyter notebooks | +The directory [examples/](https://github.com/mlco2/codecarbon/tree/master/examples) contains practical examples demonstrating how to use CodeCarbon to track carbon emissions from your computing tasks. + +The canonical, always-up-to-date index of every example lives in +[examples/README.md](https://github.com/mlco2/codecarbon/blob/master/examples/README.md), +grouped by use case: getting started, tracking parts of a run, models and +inference, sending results somewhere, configuration, hardware debugging and +clusters. It is not duplicated here so the two cannot drift apart. ## Running the Examples diff --git a/docs/how-to/installation.md b/docs/how-to/installation.md index a1113c222..7dec7215f 100644 --- a/docs/how-to/installation.md +++ b/docs/how-to/installation.md @@ -54,6 +54,10 @@ Please refer to [pyproject.toml](https://github.com/mlco2/codecarbon/blob/master/pyproject.toml) for the latest list of the packages used. +## After installing on Linux + +CodeCarbon is most accurate when it can read the Intel RAPL hardware energy counters, which are root-only by default on modern kernels. Without them it estimates CPU power from CPU load and the processor's TDP. See [Improve Measurement Accuracy with RAPL](enable-rapl.md) for a permanent, reboot-proof setup. + ## (Non-Python users) Standalone installer If you are not using Python but would like to run CodeCarbon (for instance to use the [CodeCarbon Command line](../tutorials/cli.md)), we diff --git a/docs/how-to/troubleshooting.md b/docs/how-to/troubleshooting.md new file mode 100644 index 000000000..a4624f47c --- /dev/null +++ b/docs/how-to/troubleshooting.md @@ -0,0 +1,351 @@ +# Troubleshooting + +CodeCarbon rarely crashes. When something is wrong it usually warns, falls back +to a less accurate method, and keeps going — so the first symptom is often a +number that looks odd rather than an error. This page is organized by the +message or symptom you actually see. + +If something looks wrong, first turn on debug logging so you can see what +CodeCarbon is doing: + +```python +from codecarbon import EmissionsTracker + +tracker = EmissionsTracker(log_level="debug") +``` + +or from the CLI: + +```bash +codecarbon monitor --log-level debug +``` + +Then dump what CodeCarbon detected about your machine: + +```bash +codecarbon detect +``` + +Please include the output of `codecarbon detect` in any bug report. + +## Nothing was measured / emissions are zero + +### "Another instance of codecarbon is already running. Exiting." + +CodeCarbon takes a machine-wide lock so that two trackers do not double-count +the same hardware. If a previous run crashed, a notebook cell was re-executed, +or a `codecarbon monitor` process is running in another terminal, the lock is +still held and the new tracker does nothing — `start()`, `start_task()`, +`flush()` and `stop()` all return immediately. + +What to do: + +- Check for a leftover process and stop it (`ps aux | grep codecarbon`). +- The lock is a file named `.codecarbon.lock` in the system temporary + directory — `/tmp/.codecarbon.lock` on Linux and macOS, and the equivalent of + `%TEMP%\.codecarbon.lock` on Windows (CodeCarbon uses Python's + `tempfile.gettempdir()`). If no CodeCarbon process is running, it is safe to + delete it: + + ```bash + rm /tmp/.codecarbon.lock + ``` + +- If you deliberately want several trackers at once — for example in a test + suite — set the environment variable `CODECARBON_ALLOW_MULTIPLE_RUNS=True`, or + pass `allow_multiple_runs=True` to the tracker. This is what CodeCarbon's own + test suite does. + +At debug level you will also see the related message +`Lock file /tmp/.codecarbon.lock already exists. This usually means another +instance of codecarbon is running.` + +### The tracker ran but `emissions.csv` is empty or missing + +The CSV row is written when the tracker stops or flushes. If `stop()` is never +reached — because your code raised, because the process was killed, or because +the notebook cell was interrupted — nothing is written. + +- Use the context manager or the decorator so that stopping is guaranteed even + on an exception: + + ```python + with EmissionsTracker() as tracker: + train_model() + ``` + +- Call `tracker.flush()` inside a long-running loop if you want partial results + written as you go. +- The file is written to `output_dir`, which defaults to `.` — the *current + working directory of the process*, not the directory containing your script. + If you cannot find `emissions.csv`, set `output_dir` explicitly to an + absolute path. +- Very short runs can legitimately produce values that round to zero in the + displayed precision. `measure_power_secs` defaults to 15 seconds; a run + shorter than one measurement interval has very little to report. + +## The numbers look wrong + +### "We saw that you have a ... but we don't know it. Please contact us." { #unknown-cpu-model } + +Your CPU model was detected, but it is not listed in +`codecarbon/data/hardware/cpu_power.csv`, so CodeCarbon has no TDP for it. It +falls back to a default power figure per thread, which is an estimate, not a +measurement. + +This warning literally asks you to contact the project, so please do — adding a +line to that CSV is a one-line contribution that improves accuracy for everyone +with the same chip. See the +[Contribution Guidelines](contributing.md), and include the exact CPU name from +`codecarbon detect`. + +On Linux, enabling RAPL removes the need for the TDP table entirely, because +the energy is then measured rather than estimated. See +[Improve Measurement Accuracy with RAPL](enable-rapl.md). + +### "We will use the default power consumption of ... W per thread" + +This is the fallback that follows the warning above. CodeCarbon multiplies a +fixed default power per thread by your thread count to obtain an assumed TDP, +then scales it by CPU load. + +Two independent approximations stack up here: the assumed TDP, and the +load-to-power model. Expect the result to be indicative rather than accurate, +and treat comparisons across different machines with caution. Comparisons +between two runs on the *same* machine remain meaningful, because the same +approximation applies to both. + +See [Power Estimation](../explanation/power-estimation.md) for what the +estimation model does, and [Accuracy and +validation](../explanation/accuracy.md) for how the backends compare. + +### "We were unable to detect your CPU using the `cpuinfo` package." + +CodeCarbon could not identify the CPU at all, so it cannot even look up a TDP. +The same per-thread default is used. This happens most often in containers and +virtual machines that hide CPU model information, and on some ARM platforms. + +If you know your hardware, the accurate route is to stop relying on detection: +enable RAPL on Linux, or supply your own power figures. If detection fails on a +platform where the model *is* visible, that is worth reporting as a bug — please +include the output of `codecarbon detect`. + +### Emissions seem far too high or too low + +Emissions are energy multiplied by the carbon intensity of your grid, and +energy includes a datacenter overhead factor. Check all three inputs before +concluding the measurement is wrong: + +- **Carbon intensity.** Read the `country_name`, `country_iso_code`, `region` + and `cloud_region` columns of your `emissions.csv` and confirm they describe + where the machine really is. Online mode geolocates by IP, + which is wrong for VPNs and some cloud regions. Use + `OfflineEmissionsTracker(country_iso_code="FRA")` to pin it. A grid can + legitimately differ by a factor of ten between countries, which alone + explains most surprising comparisons. +- **PUE.** The `pue` column shows the multiplier that was applied; it defaults + to 1. If you set it, everything scales by it. +- **Power.** Compare the `cpu_power`, `gpu_power` and `ram_power` columns + against what you expect for your hardware. If `cpu_power` looks like a + suspiciously round fraction of a TDP, you are on the estimation fallback + described above rather than on a measurement. + +The full column list is in [Output](../reference/output.md), and the +calculation is described in [Methodology](../explanation/methodology.md). + +## Linux: RAPL + +### "RAPL - Permission denied reading RAPL file ..." { #rapl-permission-denied } + +This is the most common Linux issue. Since a kernel security fix, the RAPL +energy counters under `/sys/class/powercap/` are root-readable only. Without +them CodeCarbon falls back to estimating from CPU load and TDP, which is +significantly less accurate. + +The warning itself suggests the quick fix: + +```bash +sudo chmod -R a+r /sys/class/powercap/* +``` + +Note that this does not survive a reboot. For the persistent udev-rule +approach and the security tradeoff involved, see +[Improve Measurement Accuracy with RAPL](enable-rapl.md). + +### "RAPL - Permission denied listing ..." / "scanning ... for RAPL domains" + +Same root cause, different point of failure: CodeCarbon could not even +enumerate the powercap directories to discover which RAPL domains exist. You +may also see `RAPL - Permission denied reading name file ...`. Same fix as +above. + +### "RAPL - No package domains found, falling back to psys" + +The full message explains the consequence: *"psys includes CPU + platform +components and may not match CPU TDP. Power readings may vary significantly +from CPU specifications."* + +RAPL exposes several domains. `package` covers a CPU socket, which is what +CodeCarbon wants to attribute to your process. `psys` (platform) covers the +whole system-on-chip and adjoining platform components — chipset, PCIe, memory +controllers and more. When only `psys` is exposed, CodeCarbon uses it, and the +readings will be higher than the CPU alone and will not line up with the CPU's +TDP. + +This is expected on several laptop platforms and on some newer Intel +generations where the package domain is not published. It is not an error, and +`psys` is generally a better answer than a load-based estimate — just do not +compare those numbers against package-domain numbers from another machine. + +If no domains at all can be selected you will instead see `RAPL - No package or +psys domains found, using all available domains`. + +See [RAPL Metrics](../explanation/rapl.md) for the domain hierarchy. + +### "RAPL - psys domain detected but not used (rapl_prefer_psys=False)" + +The mirror image of the previous section: both `package` and `psys` are +available, and CodeCarbon chose `package` because it is more consistent with +CPU TDP specifications. + +Set `rapl_prefer_psys=True` if you want total platform power instead — for +example when you are budgeting the energy of a whole machine rather than +attributing energy to one process. Expect the reported figure to rise, since it +then includes components outside the CPU package. + +### No RAPL at all: containers, VMs, WSL + +`/sys/class/powercap/` is not exposed inside most containers, in most virtual +machines, or under WSL. In all of those cases CodeCarbon silently uses the +CPU-load-and-TDP estimate; `codecarbon detect` is the quickest way to confirm +which mode you are in. + +- **Docker on bare metal.** Mount the sysfs path read-only into the container: + + ```bash + docker run --device /sys/class/powercap:/sys/class/powercap:ro + ``` + + See [Docker and Containerized + Environments](enable-rapl.md#docker-and-containerized-environments) for the + Compose form. +- **Virtual machines and cloud instances.** The hypervisor does not usually + expose RAPL to guests, and there is nothing you can do from inside the guest. + Estimation is what you get. +- **WSL.** Same situation; run on native Linux or Windows for measured values. + +Note that on shared hardware — a VM, or a container next to other containers — +RAPL would report energy for the whole physical CPU, not for your share of it. +The estimate is not merely a degraded measurement in that case; it is a +different question being answered. + +## macOS + +On Apple Silicon, CodeCarbon reads power through Apple's `powermetrics`. On +Intel Macs it can also use Intel Power Gadget, if it is installed. + +`powermetrics` requires root. CodeCarbon runs it through `sudo` and, before +using it, checks whether that `sudo` call would prompt for a password. If a +password prompt is detected, CodeCarbon logs at debug level *"Not using +PowerMetrics, sudo password prompt detected"* and falls back to estimation — +there is no way to answer an interactive prompt from inside a library. + +To get measured values, grant passwordless `sudo` for `powermetrics` alone by +editing your sudoers file, as described under +[Power usage](../explanation/methodology.md#power-usage). Run with +`log_level="debug"` to confirm the check now passes. + +### "Returncode while logging power values using Powermetrics" + +`powermetrics` started but exited with a non-zero status. Run the same command +by hand to see the real error, check that the binary is present at +`/usr/bin/powermetrics`, and confirm the sudoers entry above is still in effect +after any OS upgrade. + +## Windows + +### "Returncode while logging power values using Intel Power Gadget" + +Intel Power Gadget exited with a non-zero status, so no power values were read +from it. Intel has discontinued Intel Power Gadget, and it does not work on +recent CPU generations, so on a modern machine this is expected rather than +fixable. + +CodeCarbon's supported path on Windows is the Energy Meter Interface (EMI) +exposed by the platform driver. To see what your machine actually exposes, run: + +```bash +python examples/emi_channels.py +``` + +That script prints every EMI channel and the power each one reports, which is +the fastest way to tell whether Windows is publishing usable counters at all. +If it prints no channels, your platform does not expose EMI and CodeCarbon +falls back to estimation. + +## GPU + +### Nvidia + +CodeCarbon reads Nvidia GPUs through `nvidia-ml-py` (NVML). If NVML cannot talk +to the driver, no GPU is registered and you will see `There is no GPU +available` — GPU energy is then simply absent from the total, rather than +estimated. Check that `nvidia-smi` works as the same user; if it does not, +the problem is the driver or the container's device passthrough, not CodeCarbon. + +### AMD + +AMD GPUs are read through `amdsmi`, which ships with ROCm rather than from +PyPI. Two distinct warnings tell you which half is missing: + +- *"AMD GPU detected but amdsmi is not available. Please install amdsmi to get + GPU metrics."* — the Python module could not be imported at all. +- *"AMD GPU detected but amdsmi is not properly configured."* — the module + imported but failed to initialize. This is almost always a version mismatch + between the Python `amdsmi` package and the installed ROCm, or an outdated + driver. + +For a working ROCm setup, including how `amdsmi` is made visible to the Python +environment on a cluster, see [Run on SLURM (ROCm/PyTorch)](slurm.md) and the +scripts in `examples/slurm_rocm/`. + +### `CUDA_VISIBLE_DEVICES` / `ROCR_VISIBLE_DEVICES` and `gpu_ids` + +If you set `CUDA_VISIBLE_DEVICES` or `ROCR_VISIBLE_DEVICES`, CodeCarbon +populates `gpu_ids` from it automatically, so it measures only the GPUs your +job can see. Passing `gpu_ids` yourself overrides that. If your reported GPU +energy covers more or fewer devices than you expected, check both — one of them +is winning over the other. See the note in +[Parameters](../reference/api.md). + +## Cloud API and dashboard + +If runs do not appear on the dashboard, the tracker is still measuring +correctly — only the upload is failing. Errors from the API client are logged, +so run with `log_level="debug"` and look for messages from `ApiClient`. + +Common causes: + +- The API key or project token is missing, expired, or belongs to another + project. Re-run `codecarbon login` and `codecarbon config`. +- The API is unreachable from your network — a proxy or firewall blocking + outbound HTTPS is typical on clusters and in CI. +- The run was too short to reach an upload. An upload happens every + `api_call_interval` measurements, so a short run may end before sending + anything. + +To isolate the API from the rest of your program, run the dedicated example, +which does a tracked run with frequent API calls and verbose logging: + +```bash +python examples/api_call_debug.py +``` + +Setup and configuration are covered in +[Use the Cloud API & Dashboard](cloud-api.md). + +## Still stuck? + +Ask on [Discord](https://discord.gg/GS9js2XkJR) or +[open an issue](https://github.com/mlco2/codecarbon/issues), including the +output of `codecarbon detect` and a debug-level log. diff --git a/docs/images/og-card.png b/docs/images/og-card.png new file mode 100644 index 000000000..12253dc99 Binary files /dev/null and b/docs/images/og-card.png differ diff --git a/docs/index.md b/docs/index.md index 5dc7720ca..5abc05cba 100644 --- a/docs/index.md +++ b/docs/index.md @@ -73,9 +73,13 @@ A single datacenter can consume large amounts of energy to run computing code. A | [CLI Tutorial](tutorials/cli.md) | Track emissions from the command line | | [Python API Tutorial](tutorials/python-api.md) | Track emissions in Python code | | [Comparing Model Efficiency](tutorials/comparing-model-efficiency.md) | Measure carbon efficiency across ML models | +| [Accurate CPU measurements (Linux/RAPL)](how-to/enable-rapl.md) | Read real energy counters instead of estimating | +| [Troubleshooting](how-to/troubleshooting.md) | Fix warnings and numbers that look wrong | | [API Reference](reference/api.md) | Full parameter documentation | | [Framework Examples](how-to/scikit-learn.md) | Example usage patterns | | [Methodology](explanation/methodology.md) | How emissions are calculated | +| [Accuracy and validation](explanation/accuracy.md) | How accurate the numbers are, and why | +| [Alternatives comparison](explanation/alternatives.md) | CodeCarbon vs other carbon tracking tools | | [EcoLogits](https://ecologits.ai/latest/?utm_source=codecarbon&utm_medium=docs) | Track emissions from GenAI API calls | | [Discord Community](https://discord.gg/GS9js2XkJR) | Chat with us and the community | @@ -84,5 +88,5 @@ A single datacenter can consume large amounts of energy to run computing code. A CodeCarbon is built by a community of open-source contributors and supported by organizations committed to sustainable computing. - **[Contributors](https://github.com/mlco2/codecarbon/graphs/contributors)** - See everyone who has contributed to the project -- **[Citation](https://zenodo.org/records/11171501)** - Cite CodeCarbon in your research +- **[Citation](https://doi.org/10.5281/zenodo.4658424)** - Cite CodeCarbon in your research - **[Partners](https://github.com/mlco2/codecarbon#partners)** - The organizations supporting this work diff --git a/docs/maintaining.md b/docs/maintaining.md new file mode 100644 index 000000000..d058f2889 --- /dev/null +++ b/docs/maintaining.md @@ -0,0 +1,135 @@ +# Maintainer guide + +Maintainer-only runbook: releasing CodeCarbon, deploying the API and the dashboard, and +operating the production database. Contributors want the +[contributing guide](how-to/contributing.md) and the +[development guide](how-to/development.md) instead. + +## Release process + +- Merge all PRs. +- Open a terminal and make sure you are not in a venv with `deactivate`. +- Create a PR bumping the version with `uv run bumpver update --patch`. For a release candidate, use `uv run bumpver update --set-version 3.0.0_rc1`. +- Run `uv run python .github/pyproject_versions.py -c` to check version consistancy. +- No manual step is needed for the citation: `bumpver` also updates the `version:` line in `CITATION.cff`. Only `date-released:` may need a manual touch. +- Update the dependencies with `uv sync --upgrade` +- [Build the documentation](how-to/development.md#build-documentation) with `uv run --only-group doc task docs`. +- Push the changes. +- Merge the PR. +- Wait for the Github Action `ReleaseDrafter` to finish running on the merge commit. +- [Edit the Draft release](https://github.com/mlco2/codecarbon/releases/) on Github and give it a tag, `v1.0.0` for the version 1.0.0. Github will automatically create a Git tag for it. Complete help [here](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository). +- A [Github Action](https://github.com/mlco2/codecarbon/actions) _Upload Python Package_ will be run automaticaly to upload the package. + +### Test the release + +After the release on PyPi, please test it in a fresh environment: + +```sh +cd /tmp +rm -rf cc_rel_test +python -m venv cc_rel_test +source cc_rel_test/bin/activate +pip install codecarbon +# Check you have the last version +codecarbon --version +codecarbon monitor --offline --country-iso-code FRA +# Stop it with Ctrl+C if it works +# Then clean up +rm -rf cc_rel_test +``` + +And check if the doc looks good on [docs.codecarbon.io](https://docs.codecarbon.io/). + +### Test the build in Docker + +If you want to check the build is working, you could run: + +```bash +rm dist/* +uv build +docker run -it --rm -v $PWD:/data python:3.13 /bin/bash +pip install pytest pytest-mock requests-mock responses pandas +pip install --no-cache-dir /data/dist/codecarbon-*.whl -U --force-reinstall +cp /data/tests/test_package_integrity.py . +pytest test_package_integrity.py +``` + +## Restore database from a production Backup + +```sh +docker cp postgresql_*.dump postgres_codecarbon:/tmp +docker exec -it postgres_codecarbon bash +export BACKUP_USER=upwnpbdktjvnoks0foxq +export BACKUP_DB=bnrwiktgr4hzukt1xseg +psql -U $POSTGRES_USER -d $POSTGRES_DB -c "CREATE USER $BACKUP_USER WITH PASSWORD '$POSTGRES_PASSWORD';" +psql -U $POSTGRES_USER -d $POSTGRES_DB -c "ALTER USER $BACKUP_USER CREATEDB;" +createdb -U $BACKUP_USER $BACKUP_DB +psql -U $BACKUP_USER -d $POSTGRES_DB -c "CREATE DATABASE $BACKUP_DB;" +pg_restore -d $BACKUP_DB -U $BACKUP_USER --jobs=8 --clean --create /tmp/postgresql_*.dump +psql -U $BACKUP_USER -d $BACKUP_DB -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"$POSTGRES_USER\";" +psql -U $POSTGRES_USER -d $BACKUP_DB -c "ALTER DATABASE $POSTGRES_DB RENAME TO \"$POSTGRES_DB-backup\";" +psql -U $BACKUP_USER -d $POSTGRES_DB-backup -c "ALTER DATABASE $BACKUP_DB RENAME TO $POSTGRES_DB;" +``` + +### Clean the database + +To remove orphans (elements without run) from the database, run: + +```sql +CALL public.spcc_purgeduplicatedata(); +``` + +## Deployment + +### API + +The API is available to everyone from https://api.codecarbon.io, but if you want to deploy it for yourself, here are the instructions. + +To deploy the API we use [Clever Cloud](https://www.clever-cloud.com/), an IT Automation platform. They manage all the hard ops work while we focus on the Code Carbon value. + +Here is the Clever Cloud configuration if you want to reproduce it: + +```conf +APP_FOLDER="carbonserver" +CC_PIP_REQUIREMENTS_FILE="requirements.txt" +CC_POST_BUILD_HOOK="cd $APP_HOME/carbonserver && python3 -m alembic -c carbonserver/database/alembic.ini upgrade head" +CC_PYTHON_BACKEND="uvicorn" +CC_PYTHON_MODULE="main:app" +CC_PYTHON_VERSION="3.13" +DATABASE_URL="postgresql://secret_do_not_publish_this" +PORT="8080" +``` + +_CC stand here for Clever Cloud, not Code Carbon_ 😉 + +To deploy, + +```sh +git remote add deploy git+ssh://git@push-n2-par-clevercloud-customers.services.clever-cloud.com/app_.git +git push deploy master:master +``` + +Yeah, not so hard, is it? + +See [the doc](https://www.clever-cloud.com/doc/getting-started/quickstart/) for more informations. + +Please note that Clever Cloud host Code Carbon for free because they like our project. + +### Dashboard + +Same as for the API, for example to deploy the branch `fix-unit` to CleverCloud: + +```sh +git push clever-dashboard fix-unit:master +``` + +Config on CleverCloud: + +```sh +APP_FOLDER="dashboard" +CC_PIP_REQUIREMENTS_FILE="requirements-dashboard.txt" +CC_PYTHON_MODULE="carbon_board_API:server" +CC_PYTHON_VERSION="3.13" +CODECARBON_API_URL="https://api.codecarbon.io" +PORT="8000" +``` diff --git a/docs/reference/api.md b/docs/reference/api.md index 15117b123..77333564c 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -1,4 +1,4 @@ -# Parameters +# API Reference Parameters can be set via `EmissionsTracker()`, `OfflineEmissionsTracker()`, the `@track_emissions` decorator, config files, or environment variables. See @@ -25,6 +25,22 @@ All parameters are documented below: show_root_heading: true show_signature: false +### Methods + +::: codecarbon.emissions_tracker.BaseEmissionsTracker + options: + members: + - start + - stop + - flush + - start_task + - stop_task + - get_detected_hardware + - service_shutdown + show_root_heading: false + show_signature: true + heading_level: 4 + ## OfflineEmissionsTracker (additional parameters) `OfflineEmissionsTracker` adds these parameters for offline mode: @@ -46,3 +62,18 @@ and `country_iso_code` for offline mode: options: show_root_heading: true show_signature: false + +## Task-level tracking + +Use these to measure individual tasks inside a single run. See +`examples/task_inference.py` and `examples/task_loop_same_task.py`. + +::: codecarbon.emissions_tracker.TaskEmissionsTracker + options: + show_root_heading: true + show_signature: true + +::: codecarbon.emissions_tracker.track_task_emissions + options: + show_root_heading: true + show_signature: true diff --git a/docs/reference/deprecations.md b/docs/reference/deprecations.md new file mode 100644 index 000000000..e76bbe032 --- /dev/null +++ b/docs/reference/deprecations.md @@ -0,0 +1,75 @@ +# Deprecations and migrations + +Everything in CodeCarbon that still works but is on its way out: when it was +deprecated, when it is scheduled to be removed, and what to use instead. + +User-visible changes per release are on the +[GitHub releases page](https://github.com/mlco2/codecarbon/releases). + +| Deprecated | Since | Removal | Replacement | +|---|---|---|---| +| `codecarbon[viz-legacy]` extra | 3.3.0 | 4.0.0 | `codecarbon[carbonboard]` | +| `save_to_file`, `save_to_api`, `save_to_logger`, `save_to_prometheus`, `save_to_logfire` parameters | 3.2.8 | Not scheduled | `output_methods=[...]` | +| `co2_signal_api_token` parameter and config key | 3.1.1 | Not scheduled | `electricitymaps_api_token` | + +## `viz-legacy` extra + +The old Dash-based dashboard extra is replaced by `carbonboard`. + +```bash +# before +pip install codecarbon[viz-legacy] + +# after +pip install codecarbon[carbonboard] +``` + +See [Visualize Emissions](../how-to/visualize.md) for how to run it. + +## `save_to_*` parameters + +Passing any `save_to_*` flag raises a `DeprecationWarning`. Pass the outputs you want +as a list instead. + +```python +# mktestdocs: skip +# before +EmissionsTracker(save_to_file=True, save_to_logger=True) + +# after +from codecarbon.output_methods.base_output import OutputMethod + +EmissionsTracker(output_methods=[OutputMethod.CSV, OutputMethod.LOGGER]) +``` + +See [Output Formats](output.md) for the full list of output methods. + +## `co2_signal_api_token` + +The CO2 Signal API became part of Electricity Maps. The old parameter and config key +still work and are read as a fallback, but they log a warning. + +```python +# mktestdocs: skip +# before +EmissionsTracker(co2_signal_api_token="...") + +# after +EmissionsTracker(electricitymaps_api_token="...") +``` + +```ini +# .codecarbon.config — before +[codecarbon] +co2_signal_api_token = ... + +# .codecarbon.config — after +[codecarbon] +electricitymaps_api_token = ... +``` + +## For maintainers + +This table is the checklist for the next major release: everything with a removal +version scheduled for it must be removed, and every "Not scheduled" row should get a +decision before the release is cut. diff --git a/docs/reference/output.md b/docs/reference/output.md index 720e0a883..64122240c 100644 --- a/docs/reference/output.md +++ b/docs/reference/output.md @@ -22,7 +22,8 @@ It can also be set in the config file as a comma-separated string, e.g. The individual `save_to_file`, `save_to_api`, `save_to_logger`, `save_to_prometheus` and `save_to_logfire` parameters are deprecated and will be removed in a future version. Use `output_methods` instead. When `output_methods` - is provided, the `save_to_*` flags are ignored. + is provided, the `save_to_*` flags are ignored. See the + [deprecations list](https://docs.codecarbon.io/latest/reference/deprecations/). ## CSV diff --git a/docs/tutorials/first-tracking.md b/docs/tutorials/first-tracking.md index 82efd5a9d..6486a7e88 100644 --- a/docs/tutorials/first-tracking.md +++ b/docs/tutorials/first-tracking.md @@ -58,6 +58,7 @@ print(f"Energy consumed: {tracker.final_emissions_data.energy_consumed:.6f} kWh" ## What's next? - [Configure CodeCarbon](../how-to/configuration.md) with config files, environment variables, or script parameters +- On Linux, [get accurate CPU measurements](../how-to/enable-rapl.md) by enabling the RAPL hardware energy counters - Learn about [CLI tracking](cli.md) to monitor without code changes - Explore all [Python API options](python-api.md) (decorators, explicit objects, offline mode) - See the full [API Reference](../reference/api.md) for all configuration parameters diff --git a/examples/README.md b/examples/README.md index 3e6e022b1..070b1b45b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,19 +1,113 @@ -# Getting Started +# CodeCarbon examples + +Runnable scripts and notebooks showing how to track emissions in real workloads. + +This README is the canonical index of this directory. The documentation site +mirrors it at [docs.codecarbon.io/latest/how-to/examples/](https://docs.codecarbon.io/latest/how-to/examples/). ## Setup -The following examples use Keras from TensorFlow 2.0. The dependencies can be installed as follows +Most examples need only CodeCarbon itself: + +```bash +pip install codecarbon +``` + +Examples that train or run models need extra libraries (TensorFlow, PyTorch, +scikit-learn). Installing them all is a large download, so prefer installing +only what the example you want actually imports: ```bash pip install -r examples/requirements-examples.txt ``` -## Examples -* [mnist.py](mnist.py): Usage using explicit `CO2Tracker` objects. -* [mnist_decorator.py](mnist_decorator.py): Using the `@track_co2` decorator. -* [mnist_callback.py](mnist_callback.py): Using Keras callbacks to save emissions after each epoch. -* [mnist-comet.py](mnist-comet.py): Using `CO2Tracker` with [`Comet`](https://www.comet.ml/site) for automatic experiment and emissions tracking. -* [api_call_demo.py](api_call_demo.py): Simplest demo to send computer emissions to CodeCarbon API. -* [api_call_debug.py](api_call_debug.py): Script to send computer emissions to CodeCarbon API. Made for debugging: debug log and send data every 20 seconds. -* [emi_channels.py](emi_channels.py): Print every channel exposed by the Windows Energy Meter Interface and the power each of them reports, to debug CPU power measurement on Windows. -* And many more in the [examples](../examples) folder. +The public API used throughout is `EmissionsTracker`, `OfflineEmissionsTracker`, +`track_emissions` and `OutputMethod`. + +## Start here + +| Example | What it shows | +|---|---| +| [`mnist_context_manager.py`](mnist_context_manager.py) | The recommended entry point: `with EmissionsTracker() as tracker:` | +| [`mnist.py`](mnist.py) | Explicit `EmissionsTracker` object with `start()` and `stop()` | +| [`mnist_decorator.py`](mnist_decorator.py) | The `@track_emissions` decorator on a function | +| [`notebook.ipynb`](notebook.ipynb) | Tracking inside a Jupyter notebook | +| [`notebooks/codecarbon_workshop.ipynb`](notebooks/codecarbon_workshop.ipynb) | Full hands-on workshop covering the whole feature set | +| [`print_hardware.py`](print_hardware.py) | Dump the CPU, GPU and RAM CodeCarbon detected on this machine | + +## Tracking parts of a run + +| Example | What it shows | +|---|---| +| [`mnist_callback.py`](mnist_callback.py) | A Keras callback that records emissions after each epoch | +| [`task_inference.py`](task_inference.py) | `start_task()` / `stop_task()` to attribute emissions to named phases | +| [`task_loop_same_task.py`](task_loop_same_task.py) | Repeatedly measuring the same named task | +| [`mnist_grid_search.py`](mnist_grid_search.py) | Emissions across a hyperparameter grid search | +| [`mnist_random_search.py`](mnist_random_search.py) | Emissions across a random search | + +## Models and inference + +| Example | What it shows | +|---|---| +| [`mnist-sklearn.py`](mnist-sklearn.py) | scikit-learn training | +| [`mnist_inference.py`](mnist_inference.py) | Measuring inference rather than training | +| [`bert_inference.py`](bert_inference.py) | BERT inference with task-level tracking | +| [`transformers_smollm2.py`](transformers_smollm2.py) | Hugging Face Transformers with SmolLM2 | +| [`local_llms.ipynb`](local_llms.ipynb) | Comparing local LLM runs | +| [`ollama_local_api.py`](ollama_local_api.py) | Tracking calls to a local Ollama server | +| [`pytorch-multigpu-example.py`](pytorch-multigpu-example.py) | Multi-GPU PyTorch training | +| [`intel_npu.py`](intel_npu.py) | Inference on an Intel NPU | + +## Sending results somewhere + +| Example | What it shows | +|---|---| +| [`logging_to_file.py`](logging_to_file.py) | Writing the CodeCarbon log to a file | +| [`logging_to_file_exclusive_run.py`](logging_to_file_exclusive_run.py) | File logging for a long-running exclusive run | +| [`logging_to_google_cloud.py`](logging_to_google_cloud.py) | Google Cloud Logging output | +| [`logfire_metrics.py`](logfire_metrics.py) | Logfire metrics output | +| [`prometheus_call.py`](prometheus_call.py) | Exporting metrics to Prometheus | +| [`boamps_output.py`](boamps_output.py) | Writing output in [BoAmps](https://github.com/Boavizta/BoAmps) format | +| [`mnist-comet.py`](mnist-comet.py) | Pairing tracking with [Comet](https://www.comet.com) experiment tracking | +| [`api_call_demo.py`](api_call_demo.py) | Minimal example sending data to the CodeCarbon API | +| [`api_call_debug.py`](api_call_debug.py) | Same, with debug logging and a 20-second interval, for troubleshooting | + +## Configuration and process patterns + +| Example | What it shows | +|---|---| +| [`pue.py`](pue.py) | Applying a datacenter Power Usage Effectiveness multiplier | +| [`wue.py`](wue.py) | Applying a Water Usage Effectiveness factor | +| [`multithread.py`](multithread.py) | Several `OfflineEmissionsTracker` instances across threads | +| [`command_line_tool.py`](command_line_tool.py) | Wrapping an external binary run via `subprocess` (machine-level, not process-level) | +| [`full_cpu.py`](full_cpu.py) | Saturating the CPU to produce a clear measurement signal | + +## Hardware debugging + +Use these when the numbers look wrong and you need to see what CodeCarbon is +reading from your hardware. + +| Example | What it shows | +|---|---| +| [`compare_cpu_load_and_RAPL.py`](compare_cpu_load_and_RAPL.py) | Compare the CPU-load estimate against RAPL ground truth | +| [`compare_cpu_load_and_RAPL.ipynb`](compare_cpu_load_and_RAPL.ipynb) | The same comparison as an annotated notebook with plots | +| [`emi_channels.py`](emi_channels.py) | Print every Windows Energy Meter Interface channel and the power it reports | +| [`rapl/`](rapl/) | RAPL domain inspection and DRAM-handling diagnostics | +| [`test_rapl_calculus.sh`](test_rapl_calculus.sh) | Shell script reading raw RAPL counters around a `full_cpu.py` run | + +## Clusters + +| Example | What it shows | +|---|---| +| [`slurm_rocm/`](slurm_rocm/) | SLURM batch scripts and AMD ROCm/`amdsmi` examples | + +See also the [SLURM how-to guide](https://docs.codecarbon.io/latest/how-to/slurm/). + +## Sample data + +[`emissions.csv`](emissions.csv) is a sample output file. Use it to try the +local dashboard without running a workload first: + +```bash +carbonboard --filepath="examples/emissions.csv" --port=8050 +``` diff --git a/mkdocs.yml b/mkdocs.yml index 19c451331..8b7c9a069 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,6 +9,7 @@ edit_uri: edit/master/docs/ copyright: "© CodeCarbon" theme: + custom_dir: overrides favicon: images/favicon.ico logo: images/codecarbon-logo.svg palette: @@ -133,6 +134,18 @@ extra: - icon: fontawesome/brands/github link: https://github.com/mlco2/codecarbon name: CodeCarbon on GitHub + - icon: fontawesome/brands/discord + link: https://discord.gg/GS9js2XkJR + name: Join the CodeCarbon Discord + - icon: fontawesome/brands/python + link: https://pypi.org/project/codecarbon/ + name: CodeCarbon on PyPI + - icon: fontawesome/solid/globe + link: https://codecarbon.io + name: CodeCarbon website + - icon: fontawesome/solid/chart-line + link: https://dashboard.codecarbon.io + name: CodeCarbon dashboard nav: - Home: index.md @@ -147,8 +160,10 @@ nav: - HuggingFace Diffusers: how-to/diffusers.md - LLMs and Agents: how-to/agents.md - How-to Guides: + - Troubleshooting: how-to/troubleshooting.md - Examples: how-to/examples.md - Configure CodeCarbon: how-to/configuration.md + - Get accurate CPU measurements (Linux/RAPL): how-to/enable-rapl.md - Compare Model Efficiency: tutorials/comparing-model-efficiency.md - Dashboard & Visualization: - Use the Cloud API & Dashboard: how-to/cloud-api.md @@ -157,7 +172,6 @@ nav: - Log to External Systems: how-to/logging.md - Integrate with Comet: how-to/comet.md - Deployment: - - Improve Measurement Accuracy with RAPL: how-to/enable-rapl.md - Deploy as a Linux Service: how-to/linux-service.md - Deploy with Ansible: how-to/ansible.md - Run on SLURM (ROCm/PyTorch): how-to/slurm.md @@ -165,6 +179,8 @@ nav: - Contributing: - Code of Conduct: how-to/code-of-conduct.md - Contribution Guidelines: how-to/contributing.md + - Development Guide: how-to/development.md + - Maintainer Guide: maintaining.md - AI Policy: how-to/ai_policy.md - Working with Coding Agent: how-to/working_with_agent.md - Agent Instructions: how-to/agent-instructions.md @@ -172,7 +188,9 @@ nav: - Explanation: - Why CodeCarbon: explanation/why.md - When to Use CodeCarbon vs EcoLogits: explanation/when-to-use.md + - CodeCarbon vs Alternatives: explanation/alternatives.md - Methodology: explanation/methodology.md + - Accuracy and Validation: explanation/accuracy.md - RAPL Metrics: explanation/rapl.md - Power Estimation: explanation/power-estimation.md - Model Comparisons: explanation/model-comparisons.md @@ -180,6 +198,7 @@ nav: - Reference: - API Reference: reference/api.md - Output Formats: reference/output.md + - Deprecations and Migrations: reference/deprecations.md - CLI Reference: reference/cli.md - Track GenAI API Calls (EcoLogits) ↗: https://ecologits.ai/latest/?utm_source=codecarbon&utm_medium=docs - Join Our Discord 💬 ↗: https://discord.gg/GS9js2XkJR diff --git a/overrides/main.html b/overrides/main.html new file mode 100644 index 000000000..1d3956df5 --- /dev/null +++ b/overrides/main.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} + + +{% block extrahead %} + {% set title = config.site_name %} + {% if page.meta and page.meta.title %} + {% set title = page.meta.title ~ " - " ~ config.site_name %} + {% elif page.title and not page.is_homepage %} + {% set title = page.title | striptags ~ " - " ~ config.site_name %} + {% endif %} + {% set description = config.site_description %} + {% if page.meta and page.meta.description %} + {% set description = page.meta.description %} + {% endif %} + {% set image = config.site_url ~ "images/og-card.png" %} + + + + + + + + + + {% if page.canonical_url %} + + {% endif %} + + + + + +{% endblock %} diff --git a/pyproject.toml b/pyproject.toml index 26a360338..2f4976758 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,10 +5,28 @@ build-backend = "setuptools.build_meta" [project] name = "codecarbon" dynamic = ["version"] +description = "Track and reduce the CO2 emissions of your computing" readme = "README.md" requires-python = ">=3.10" license = "MIT" license-files = ["LICENSE"] +keywords = [ + "carbon", + "co2", + "emissions", + "carbon-footprint", + "sustainability", + "green-ai", + "green-computing", + "energy", + "energy-consumption", + "power-consumption", + "rapl", + "gpu", + "machine-learning", + "mlops", + "monitoring", +] authors = [ { name = "Mila" }, { name = "DataForGood" }, @@ -17,12 +35,24 @@ authors = [ { name = "Haverford College" }, ] classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Intended Audience :: System Administrators", "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: System :: Monitoring", + "Topic :: System :: Benchmark", + "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ "arrow", @@ -158,6 +188,15 @@ version_pattern = "MAJOR.MINOR.PATCH[_TAGNUM]" "codecarbon/_version.py" = [ '^__version__ = "{version}"$', ] +"CITATION.cff" = [ + '^version: {version}$', +] + +[tool.black] +# Pin the target rather than letting black infer it from requires-python: the +# inferred upper bound outran the interpreter, and black then skips its AST +# safety check. Keep this at the oldest supported Python. +target-version = ["py310"] [tool.pytest.ini_options] pythonpath = "." diff --git a/tests/test_emissions_tracker.py b/tests/test_emissions_tracker.py index 8ab12e5d8..4afb820ca 100644 --- a/tests/test_emissions_tracker.py +++ b/tests/test_emissions_tracker.py @@ -16,6 +16,7 @@ EmissionsTracker, OfflineEmissionsTracker, track_emissions, + track_task_emissions, ) from codecarbon.external.geography import CloudMetadata from codecarbon.output import BoAmpsOutput, CodeCarbonAPIOutput, OutputMethod @@ -609,6 +610,38 @@ def dummy_train_model(): dummy_train_model() self.verify_output_file(self.emissions_file_path, 2) + def test_track_task_emissions_decorator( + self, + mock_cli_setup, + mock_log_values, + mocked_get_gpu_details, + mocked_env_cloud_details, + mocked_get_gpu_utilization_list, + mocked_is_gpu_details_available, + mocked_is_nvidia_system, + ): + tracker = OfflineEmissionsTracker( + country_iso_code="USA", + measure_power_secs=1, + output_dir=self.temp_path, + experiment_id="test", + ) + tracker.start() + + @track_task_emissions(tracker=tracker, task_name="training") + def dummy_train_model(): + heavy_computation(run_time_secs=1) + return 42 + + # The decorator is transparent: same name, same return value. + self.assertEqual("dummy_train_model", dummy_train_model.__name__) + self.assertEqual(42, dummy_train_model()) + + # ...and the task has been measured on the tracker we passed in. + self.assertIn("training", tracker._tasks) + self.assertGreater(tracker._tasks["training"].emissions_data.duration, 0) + tracker.stop() + def test_offline_tracker_country_name( self, mock_cli_setup, diff --git a/tests/test_rapl_permissions.py b/tests/test_rapl_permissions.py index 4c522435d..f44579e9a 100644 --- a/tests/test_rapl_permissions.py +++ b/tests/test_rapl_permissions.py @@ -2,10 +2,49 @@ import os import stat import sys +from unittest import mock import pytest -from codecarbon.core.cpu import IntelRAPL, is_rapl_available +from codecarbon.core.cpu import ( + RAPL_PERMISSION_HELP, + IntelRAPL, + _create_warn_function, + is_rapl_available, +) + + +def test_permission_warning_is_emitted_once(caplog): + """The scan hits every domain: warn once, then stay quiet on the debug channel.""" + warn = _create_warn_function() + + with caplog.at_level(logging.DEBUG, logger="codecarbon"): + warn("/sys/class/powercap/intel-rapl:0/energy_uj") + warn("/sys/class/powercap/intel-rapl:1/energy_uj") + + levels = [r.levelno for r in caplog.records] + assert levels == [logging.WARNING, logging.DEBUG] + # Both messages tell the user how to fix the permissions. + for record in caplog.records: + assert RAPL_PERMISSION_HELP in record.getMessage() + + +def test_unreadable_domain_is_skipped_with_actionable_warning(caplog): + rapl = IntelRAPL.__new__(IntelRAPL) # no filesystem scan, we test the check alone + + with ( + mock.patch("builtins.open", side_effect=PermissionError("nope")), + caplog.at_level(logging.WARNING, logger="codecarbon"), + ): + readable, is_main = rapl._validate_domain_readable( + "/sys/class/powercap/intel-rapl:0/energy_uj", + "/sys/class/powercap/intel-rapl:0", + "package-0", + ) + + # An unreadable domain is skipped, and never reported as the main one. + assert (readable, is_main) == (False, False) + assert RAPL_PERMISSION_HELP in caplog.text @pytest.mark.skipif(not sys.platform.lower().startswith("lin"), reason="requires Linux") diff --git a/tests/test_readme_links.py b/tests/test_readme_links.py new file mode 100644 index 000000000..3614c3b44 --- /dev/null +++ b/tests/test_readme_links.py @@ -0,0 +1,26 @@ +"""Guard against README indexes referencing files that no longer exist.""" + +import re +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).parent.parent +# Markdown files whose relative links must all resolve. +CHECKED_FILES = [REPO_ROOT / "examples" / "README.md"] + +LINK_RE = re.compile(r"\[[^\]]*\]\(([^)]+)\)") + + +@pytest.mark.parametrize("md_file", CHECKED_FILES, ids=lambda p: str(p.name)) +def test_relative_links_exist(md_file): + if not md_file.exists(): + # The wheel-validation job runs the tests without the rest of the repository. + pytest.skip(f"{md_file} is not present in this checkout") + missing = [ + target + for target in LINK_RE.findall(md_file.read_text()) + if not target.startswith(("http://", "https://", "mailto:", "#")) + and not (md_file.parent / target.split("#")[0]).exists() + ] + assert not missing, f"{md_file} links to missing paths: {missing}" diff --git a/uv.lock b/uv.lock index f37050957..31bcb75e7 100644 --- a/uv.lock +++ b/uv.lock @@ -3535,7 +3535,7 @@ wheels = [ [[package]] name = "zensical" -version = "0.0.52" +version = "0.0.53" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -3547,20 +3547,20 @@ dependencies = [ { name = "pyyaml" }, { name = "tomli" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/53/f3657dc0ed7666b29cededfeb424b28b8cf1f6ca75f7066af76fca8c1bcf/zensical-0.0.52.tar.gz", hash = "sha256:b11b79dd1bb7da4c1a5293cbc5a2f4394d980bf2bf1c4c326062bc5ddcf2a2e8", size = 3991761, upload-time = "2026-07-30T10:22:51.45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/32/e143d094f832d8a2de6b56f2e20c40437d06376be69117d8e725d00e22c4/zensical-0.0.52-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:7e5fa1df686af8ef223d16637fd31fe2ab248a8b40556037c50af1102b05f5ed", size = 12839791, upload-time = "2026-07-30T10:22:24.867Z" }, - { url = "https://files.pythonhosted.org/packages/13/e6/746c00830a4149826190f99e182cf5642745978d8702372e8370c7ec8a12/zensical-0.0.52-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:20117f935e23900411e5d03ef1d15b3e5ef3f8730d9096a49eb08754fef1f2d4", size = 12723378, upload-time = "2026-07-30T10:22:27.057Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5b/904fa8d57dd27682dcd2a8c683661f0d6e12c111d150b34c7d568ac80018/zensical-0.0.52-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d889b32121fa43061a902c49d976353a674ae566803ac0ea5d41f01aa5da131", size = 13173818, upload-time = "2026-07-30T10:22:29.168Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/f124a2a512ae0d5d9d158b1cf31798e3191eb9a21ac8ccfdc9cc38e09a7a/zensical-0.0.52-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61b62d254d47e82fb687bc8a74a1f0220a900a0f3ca4bb6c93eab51ca7c8391a", size = 13111975, upload-time = "2026-07-30T10:22:31.418Z" }, - { url = "https://files.pythonhosted.org/packages/10/09/bdcb062263005e0430a532e72ea32a24955208ae92191f02d38abd58b793/zensical-0.0.52-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d09e7fd0d80418639482212fbce19d09877cbef92a4122030e5d19b32dbea08", size = 13498189, upload-time = "2026-07-30T10:22:33.42Z" }, - { url = "https://files.pythonhosted.org/packages/5a/60/7c3d6cee180a65e06a22de8a143d9ae4791ebae5278fd1abd2977fdc69b0/zensical-0.0.52-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df854d07f5d89a47f37f661058bc3f95efc64d45b0d5500ea46821244f69c16", size = 13145652, upload-time = "2026-07-30T10:22:35.871Z" }, - { url = "https://files.pythonhosted.org/packages/1d/76/e794e77745017652344463a3c4ba286073ca26a43b0c86dcb40ae0dac0b5/zensical-0.0.52-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:188e15376b3718e6e880751c4014e394b59f798329bc21e39c950cc58254a32c", size = 13349087, upload-time = "2026-07-30T10:22:38.005Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ba/6a38f9c29392c1d5729b25d24c8526d3f86c8133bbd2d8481acf1d1bdc78/zensical-0.0.52-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:40cde85bf35901a4c14a56349502b6d3c754d4386de6b887d498cd7269757e00", size = 13385257, upload-time = "2026-07-30T10:22:40.785Z" }, - { url = "https://files.pythonhosted.org/packages/02/b6/c82317c747ec39e1557aeb2b762087bc22437f80a07a16df21df666ac250/zensical-0.0.52-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:d26c29272ce5bad16564a19ecdfd43bbd9b41568a57b923e8b710359de633322", size = 13550906, upload-time = "2026-07-30T10:22:42.933Z" }, - { url = "https://files.pythonhosted.org/packages/c6/77/b7c83ddced2887b03113c322036f74a2d519ae5599cbb2662e8d7f5c7e2c/zensical-0.0.52-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a1e1c6c99ae50e98cac957bb48719aecec57aa47899462acd65b2fa9afcbcdd", size = 13485819, upload-time = "2026-07-30T10:22:45.001Z" }, - { url = "https://files.pythonhosted.org/packages/b8/88/fcaee358b7e9d380ccdeb6d3a434b24cd6df6b48ff077bdda0046d8fb6c0/zensical-0.0.52-cp310-abi3-win32.whl", hash = "sha256:4ef40c8d2e8fc84886a28704667e38b7f89663cff32a57db5e909a26cf5cf66a", size = 12410758, upload-time = "2026-07-30T10:22:47.28Z" }, - { url = "https://files.pythonhosted.org/packages/9d/1c/d410a93763cafb8827e4e318bad369b84068047c4d658b511c9307104a62/zensical-0.0.52-cp310-abi3-win_amd64.whl", hash = "sha256:dd904e316f1cdc4fee707febdd85d0ac13f742a8ba14da9f9a4dacb8603fe480", size = 12662400, upload-time = "2026-07-30T10:22:49.503Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/bc/8b/d916d8226738421a847f039f71278fd07789744c32e9b40abcfa8b849ad8/zensical-0.0.53.tar.gz", hash = "sha256:61672d3e6389822b5738e099816dbc07416ea84db67c2b1cb7e6ea977d2e04d7", size = 3988318, upload-time = "2026-08-04T14:08:54.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/53/5db8c8e5a257db9a5fff0b77c8e05783283d6aaf42c05e34577f6b59f5d0/zensical-0.0.53-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:292cf9c7c323a50c6e3515d334ca08d9dcc517ce6d9d8ad1cd94d22befab1f56", size = 12835291, upload-time = "2026-08-04T14:08:16.746Z" }, + { url = "https://files.pythonhosted.org/packages/33/73/49a64c2c44aec251336a1cedcccbec7ba3d3eba9dd75d52ed24c09217d86/zensical-0.0.53-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0f4c1219c534d3cccc0b86093748dc009e0e9d80d4dad8d65e2150c846aa1123", size = 12719959, upload-time = "2026-08-04T14:08:20.279Z" }, + { url = "https://files.pythonhosted.org/packages/d8/3a/2c08429f7c725d1a40d158b84d6aca4b5c4320d09a0a313e875d7dd3bfe5/zensical-0.0.53-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ca63b952b4961461b4376d61603adc2bf9d81b4df4946b2f27e20b2726f881f", size = 13169416, upload-time = "2026-08-04T14:08:23.474Z" }, + { url = "https://files.pythonhosted.org/packages/41/bc/ed057082989645d5ad3245bdf0b14c30334a315f866552c794c2413cf92f/zensical-0.0.53-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34f41b7f37a0430a1378ac13d9a72513fcc53db676c124378cf63cc6f6e22713", size = 13099720, upload-time = "2026-08-04T14:08:26.521Z" }, + { url = "https://files.pythonhosted.org/packages/a4/54/859cf2267ef853ff20eee2af37d898071f821bf30ec3df7d73061b391c78/zensical-0.0.53-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30ed22e9fcedda71888d9fe84f4fdb1aadd3b66cdb0223716f1eecce9ae22b07", size = 13482295, upload-time = "2026-08-04T14:08:29.618Z" }, + { url = "https://files.pythonhosted.org/packages/35/94/f73744d9f4b6107e2740aad58214285b84d4cf0997cde36bced43089b3d0/zensical-0.0.53-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b0cb72861b14bd985bc5ad0203c35b1da7a19c87c194df3189fab7a910db04", size = 13140985, upload-time = "2026-08-04T14:08:32.731Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ef/7557d859e25e4a74214d718a1528f2a123ff9d84823b49b32df9bc41ef17/zensical-0.0.53-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:15e9813f0f59db6cf1316414301139d030f1690b68af645f1bf68d78bc3defe0", size = 13344554, upload-time = "2026-08-04T14:08:35.924Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d9/3a1011bd4390e85a6f602afca6ff8b862454415a800e7b41471dadd9e6b1/zensical-0.0.53-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c203493598d6cad890d7cb48f9d75693f648fe0d2347b2f147406a99fd7bb101", size = 13373180, upload-time = "2026-08-04T14:08:39.384Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ee/f4faf3d66d1e854afa43fa5354c1e0c8414af3fc5c563233a3ca7f10e494/zensical-0.0.53-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:ec34844b3bc1855f5c10b99efbeebd27abcd983a9144dbad965609e65915c050", size = 13531133, upload-time = "2026-08-04T14:08:42.679Z" }, + { url = "https://files.pythonhosted.org/packages/5e/98/4a0272bb79bdd326714e552f685d58f31a501ffd49bdc17ae187e92b2581/zensical-0.0.53-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e016062c3299c84be811848d1e81ad0f3f711615f0bed87bb0a1b47f6968a5a4", size = 13480141, upload-time = "2026-08-04T14:08:45.97Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ab/8cbceea1e7f4d6d2ac078a0c34ccd06f1419401248d22f6d6ded4ac9a443/zensical-0.0.53-cp310-abi3-win32.whl", hash = "sha256:abb0af33bb646f15224045baa6c4118b59a2c9c3f80d7cd48edd66ee961c1985", size = 12410234, upload-time = "2026-08-04T14:08:48.871Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ac/65f0ced38274b6c1073a4b1c52ea41b8b43e7e972f5e3979c2f2aca5cc46/zensical-0.0.53-cp310-abi3-win_amd64.whl", hash = "sha256:8b609bc89717b6f276774651ea3a41df21b4813929d2a206ee161a294dc28cd1", size = 12646224, upload-time = "2026-08-04T14:08:51.945Z" }, ] [[package]]