An important step in trying to improve the energy efficiency of your software is to measure the actual energy consumed during the execution of your code.
There are a handful of open-source software packages available online to measure the energy consumption of a piece of code, including PowerAPI, EnergyMon or PMT. In this hands-on exercice, we will focus on CodeCarbon, a Python package readily available on Linux, Windows and MacOS.
We will start by testing CodeCarbon on a simple example code provided below, then provide guidance on how to test a snippet of your own code. For the later, we advise selecting a well-contained, single-purpose function at first and progressively extend to larger and more complex workflows. The runtime of your selected function/program should be of the order of ~10s since CodeCarbon is not designed for high frequency measurments.
Code Carbon is written in Python and brings a number of dependencies. It is recommended to setup a dedicated environment using a package and environment management tool such as Conda to avoid disrupting the packages currently available on your computer. However, this step is not mandatory.
Create a new Conda environment:
conda create -n greencomputing python=3.12
Activate the new environment:
conda activate greencomputing
Use the package manager 'pip' to get CodeCarbon (and its dependencies):
pip install codecarbon
In this case, we assume that Python is already available on your computer.
python -m pip install codecarbon
To check that CodeCarbon is effectively installed, try the following command:
python -c "from codecarbon import OfflineEmissionsTracker; print('SUCCESS')"
If CodeCarbon was successfully installed, the command will simply return 'SUCCESS'.
The above installation instructions should work on Windows if using WSL.
If not, it is generally easiest to install python packages using conda which can be installed according to these instructions.
If you have another setup or requirements then please ask - we will do our best to help you get set up!
The test case consists in computing the Euclidian distance between all the points contained in a list, where each point is defined by two coordinates. To illustrate how changes to the code can impact the energy consumption, two implementations of the computation are provided: a naive version relying on Python's list and a version using Numpy. The code can be obtained here.
Let's now write a small Python driver to execute the computation and measure the energy consumption.
We will start by importing the Python modules needed. In a new python file (e.g. EDCodeCarbonTest.py), write the following:
import random
import numpy as np
from EuclidianDistance import get_distances
from codecarbon import OfflineEmissionsTracker
The first two modules are needed in order to generate data. From the
code provided above, we will use the get_distances
function, and
finally we will use CodeCarbon's OfflineEmissionsTracker
.
Note that CodeCarbon can also be used Online
, allowing live access to
carbon intensity data and a dashboard, but the setup is more complicated.
Once the modules are loaded, we can write the main function of our program, appending to the file created above:
if __name__ == "__main__":
# Prepare a list of coordinates
# Define the list length
n_npts = 20000
# Get Numpy's random number generator with a fixed seed
rng = np.random.default_rng(12345)
# Populate the list, with coordinates uniformly sampled
# in the [0,1] interval.
points = []
for _ in range(n_npts):
points.append((rng.uniform(0.0, 1.0), rng.uniform(0.0, 1.0)))
We now have a list of points to pass to the get_distances
function.
But we first need to initialize CodeCarbon emission tracker. In this example,
we will use a context manager such as:
with OfflineEmissionsTracker(country_iso_code="NLD",
measure_power_secs=5) as tracker:
>> compute intensive code ...
where the tracker
will directly start and report energy consumption as
we execute the code. There are numerous runtime options available when
initializing the OfflineEmissionsTracker
, refers to CodeCarbon
documentation for a complete
overview. In the example above we only provided the Netherlands ISO 3 letters code,
and the interval (in seconds) to measure hardware power usage.
Let's now add our actual computation in the code as follows:
with OfflineEmissionsTracker(country_iso_code="NLD",
measure_power_secs=5) as tracker:
get_distances(points, "base")
where we use the base implementation of the Euclidean distance calculation. It is now time to run the program.
Note
: On MacOS, CodeCarbon relies on PowerMetrics to access hardware power data, thus requiring root/sudo access. You will be prompted for your login password
The actual output of running the Python program will depend on your platform and hardware, but it will resemble the following (here on MacOS):
[...] [setup] RAM Tracking...
[...] [setup] GPU Tracking...
[...] No GPU found.
[...] [setup] CPU Tracking...
[...] Tracking Apple CPU and GPU via PowerMetrics
[...] >>> Tracker's metadata:
[...] Platform system: macOS-14.6.1-arm64-arm-64bit
[...] Python version: 3.12.7
[...] CodeCarbon version: 2.7.1
[...] Available RAM : 24.000 GB
[...] CPU count: 8
[...] CPU model: Apple M2
[...] GPU count: 1
[...] GPU model: Apple M2
[...] Saving emissions data to file ./NLeSC/Training/CodeCarbon/emissions.csv
[...] Energy consumed for RAM : 0.000013 kWh. RAM Power : 9.000000000000002 W
[...] Energy consumed for all CPUs : 0.000010 kWh. Total CPU Power : 7.500399999999999 W
[...] Energy consumed for all GPUs : 0.000000 kWh. Total GPU Power : 0.001 W
[...] Energy consumed for RAM : 0.000038 kWh. RAM Power : 9.000000000000002 W
[...] 0.000048 kWh of electricity used since the beginning.
[...] Energy consumed for all CPUs : 0.000033 kWh. Total CPU Power : 8.2367 W
[...] Energy consumed for all GPUs : 0.000000 kWh. Total GPU Power : 0.002 W
[...] Energy consumed for RAM : 0.000048 kWh. RAM Power : 9.000000000000002 W
[...] ...
[...] 0.000321 kWh of electricity used since the beginning.
[...] 0.000890 g.CO2eq/s mean an estimation of 28.064960638276645 kg.CO2eq/year
Note
: If the CPU on your system is not recognized by CodeCarbon, a default CPU will be used but the code will issue Warnings.
At time interval specified when initializing the OfflineEmissionsTracker
, power and energy
are reported for the various hardware. Based on the country code provided, an estimate of
the CO2eq emission rate is also reported at the end. As you can see, a constant 9W power is used for the RAM here, whereas
small fluctuations are observed on the CPU side. Even though the RAM and CPU are shared with other programs running
on your computer, CodeCarbon assign their full usage to the measured code. A more accurate measurement of the specific
process CPU usage can be obtained by providing an extra argument to the tracker initialization, tracking_mode = "process"
.
However, this method does not provide a good measurement of the memory power.
Let's now switch to the Numpy version of program by updating the call to the get_distances
function:
get_distances(points, "numpy")
And re-run the program. The measured energy consumption for thid case is 0.000194 kWh, for an estimated emission rate of 0.000538 g.CO2eq/s. This small example demonstrate how by relying on a more efficient implementation, the energy consumption and associated emissions can be reduced, mostly due to a shorter runtime. Feel free to experiment with the following:
OfflineEmissionsTracker
, changing the country code for instanceIf your target code is based on Python, the simplest way to adapt the small example provided above to
your case is wrap your entire program into a single function call and replace the get_distances
function
by your own.
An alternate solution, applicable to softwares written in other programming language such as R or C++, is to
rely on Python subprocess. In the following, let's assume that you have an executable R file (i.e.
with #! /usr/bin/Rscript
on the first line).
Create a new python file (e.g. CodeCarbonWrap.py), with the following imports:
import subprocess
from codecarbon import OfflineEmissionsTracker
and the following main function:
if __name__ == "__main__":
with OfflineEmissionsTracker(country_iso_code="NLD",
measure_power_secs=5) as tracker:
subprocess.call (["/usr/bin/Rscript", "--vanilla", "/pathto/MyrScript.r"])
Note that because the script R script is executed in an external process, the option tracking_mode = "process"
is no longer adapted and only the mode where the CPU usage of the entire computer is measured is relevant. You
should thus be aware of the other programs running on your computer (e.g. a web brower with video streaming) as their
CPU usage will be measured as well, and for more accurate measurements those programs should be terminated.
In this short tutorial, you have tested first-hand how to measure the energy consumption of a piece of code using CodeCarbon. CodeCarbon can be deployed on larger application as well as on HPC platforms. Although written in Python, CodeCarbon can be used for any other languages as long as you are able to use Python subprocess to execute the external program (i.e. R, C++, Fortran, ...).