GPU, energy and CO2 metrics¶
Node only. The browser and React Native entries report no GPU, energy or CO2 metric: WebGPU exposes no utilisation or power, and the device's own counters are not reachable.
What is measured, and how¶
nvidia-smi --query-gpu=... --format=csv,noheader,nounits is run every
GENAI_GPU_COLLECTION_INTERVAL seconds (default 5), off the request path, with a
10-second timeout. One line per device. The gauges report the latest sample.
flushTelemetry() waits for the first sample, so a run shorter than one nvidia-smi call
still exports a GPU reading.
The timeout is the SDK's own, not execFile's. execFile's timeout is a timer: when the
application blocks the event loop past it while nvidia-smi runs - an ES-module application
evaluating its modules right after instrument() - the timer fires before the child's exit
and output are read, the finished child is "killed", and its output is lost with no error.
Up to 0.1.0-dev that read as "no GPU on this host", logged at info level, and the process
reported no GPU telemetry at all. Here a timer that fires defers to after pending I/O, and
a child that has exited is never killed.
| Metric | Unit | From |
|---|---|---|
gen_ai.gpu.utilization, gen_ai.gpu.memory.utilization |
% | utilization.gpu, utilization.memory |
gen_ai.gpu.memory.used, gen_ai.gpu.memory.total |
MiB | memory.used, memory.total |
gen_ai.gpu.temperature |
Cel | temperature.gpu |
gen_ai.gpu.power, gen_ai.gpu.power.limit |
W | power.draw, power.limit |
gen_ai.gpu.clock.sm, gen_ai.gpu.clock.memory |
MHz | clocks.current.sm, clocks.current.memory |
gen_ai.gpu.fan.speed |
% | fan.speed |
gen_ai.gpu.performance.state |
0 to 15 | pstate |
gen_ai.gpu.throttle.thermal, .power, .hw_slowdown |
1 or 0 | clocks_throttle_reasons.* |
gen_ai.gpu.ecc.errors.corrected, .uncorrected |
count | ecc.errors.*.volatile.total |
gen_ai.gpu.aggregate.mean_utilization, .total_memory_used (GiB), .total_power, .max_temperature |
over the devices that reported the value; gpu_count says how many |
Per-device points carry gpu_id and gpu_name.
From power, between two samples:
| Metric | |
|---|---|
gen_ai.energy.consumed (component = gpu), gen_ai.energy.total |
kWh: mean power of the interval's two ends, times its length |
gen_ai.power.consumption |
W, a histogram of the readings |
gen_ai.power.cost |
USD: that energy times GENAI_POWER_COST_PER_KWH |
gen_ai.co2.emissions, gen_ai.co2.emissions_rate |
gCO2e, gCO2e/s: that energy times GENAI_CARBON_INTENSITY. Only with GENAI_ENABLE_CO2_TRACKING=true |
What is not measured is not reported¶
| Situation | What happens |
|---|---|
No nvidia-smi on the host |
No GPU instrument is created. One line at info level. Not asked again |
nvidia-smi answers "No devices were found" |
As a host with no GPU: no instrument, one line at info level |
nvidia-smi present and failing at start (no driver, a timeout, no output at all) |
No instrument yet. One warning with what the tool said; the first sample is tried again every interval, and after 5 failures in a row one more warning and no further attempt. An empty answer is a failure, never "no GPU" |
A field the device reports as [N/A] (on a laptop GPU: power limit, fan speed, ECC) |
That gauge has no point for that device. The others do |
| An older driver that does not know one of the fields | The core set (utilisation, memory, temperature, power) is used from then on |
| The tool starts failing later | Nothing is observed for that sample, and the energy of the gap is not integrated. After 5 failed samples in a row collection stops, with one warning |
| A device with no power reading | No energy, cost or CO2 for it |
| CPU and RAM energy | Not measured, so not reported. gen_ai.energy.total is GPU energy only, and says nothing else |
A collector that stopped shows as a gap, not as a flat line: the exporter asks the
metrics SDK for delta temporality for gauges (src/gauge-temporality.ts), so a gauge
nobody observes any more is not exported. Under the SDK's default cumulative temporality
it would go on exporting its last value. A gauge has no temporality on the wire, so nothing
else changes.
CO2 is a factor, not a reading of the grid¶
gen_ai.co2.emissions is measured energy multiplied by one number,
GENAI_CARBON_INTENSITY, in gCO2e per kWh. Its default, 475, is a world average and is
the reference implementation's default. It is not the intensity of the grid the host is
on. Set it to that grid's figure, or treat the metric as an order of magnitude.
The reference uses codecarbon, which carries regional grid data and also measures CPU and
RAM. None of that exists here. The GENAI_CO2_* location and mode variables are accepted
and have no effect; setting one says so in the log at start-up.
Differences from the reference¶
- The reference's own formula for energy from power is 3.6 times too high (it multiplies
kilowatts by seconds and treats the result as watt-hours); it affects its
gen_ai.power.costalways, and its CO2 when codecarbon is not installed. Here the energy is watts times seconds over 3 600 000. Confirmed, SDK_ADDITIONS.md section 9. gen_ai.gpu.pcie.tx/.rxandgen_ai.codecarbon.task.durationare never reported.- AMD GPUs are not read. The reference reads them through the
amdsmiPython binding; the command-line tool's output could not be checked on any machine available here.
What was run¶
The parser and the collector are tested against the real output of nvidia-smi on one
machine (RTX 5090 Laptop GPU, driver 592.02, Windows), and the real tool was run through
NvidiaSmiSource there. A two-device case uses a hand-written fixture in the same
format. Not run: a multi-GPU host, a data-centre GPU, Linux, an older driver.
Another source of readings¶
setGpuSource(() => source) replaces nvidia-smi with anything that implements
sample(): Promise<GpuSample[] | undefined> - a vendor tool, an exporter already running
on the host. A factory that returns undefined switches GPU reading off.