How to measure GPU energy in Python
On this page
The short answerSeveral steps in one programWhy not read nvidia-smi or the power reading?Make the number mean somethingMeasure a whole script insteadNextYou can read a GPU's real energy use from Python in a few lines, without estimating it from utilisation or a spec sheet. This guide shows how, and what to watch out for so the number means something.
The short answer
pip install landauer-gap
import joules
with joules.measure("train") as p:
train_one_epoch()
print(p.energy_j, "J in", p.duration_s, "s")
joules.measure reads the hardware's own energy counter when the block starts and when it ends. The energy of the block is the difference. On NVIDIA GPUs from Volta onwards that counter comes from the driver (NVML); on Intel and AMD CPUs, from RAPL; on AMD and Intel GPUs, from the kernel's hwmon files.
Several steps in one program
To compare the steps of a longer program, give each one a name. A Session keeps a median and a spread for every name:
import joules
with joules.Session() as s:
for batch in loader:
with s.phase("forward"):
out = model(batch)
with s.phase("backward"):
out.loss.backward()
print(s.summary()) # per phase: count, median energy and time, quartiles
Why not read nvidia-smi or the power reading?
- Power is not energy. Energy is power over time. Adding up power readings taken every so often misses whatever happened between them.
- The power reading is smoothed. On many recent GPUs it is averaged over a window, so short kernels and spikes are blurred or missed. The energy counter keeps a running total instead.
- Utilisation is not efficiency. A GPU can report 90%+ busy while it is power-capped and running at a lower clock. The work took longer and the energy per result went up, but utilisation looks fine.
Make the number mean something
- Measure long enough. Counters update every few milliseconds on GPUs. Measure blocks of about a second or more; loop short work several times.
- Subtract idle. A GPU draws power even when it does nothing.
joules run --subtract-idle 5 -- python train.pymeasures the idle machine first and reports energy above idle as well as the total. - Compare fairly. When you compare two versions, alternate them (A B A B) rather than running all of A then all of B, so heat and clock changes hit both equally.
joules compare "python a.py" "python b.py"does this and reports medians with a significance test. - Record the conditions. Clocks, temperature and throttling change results. joules records them with every measurement.
Measure a whole script instead
No code changes at all:
joules -- python train.py
It prints the energy of each GPU, the CPU and DRAM, the time, the cost and the carbon.