Medir la potencia de una GPU: NVML, DCGM y los errores de muestreo que invalidan tus vatios

Notación: importes en euros (N €), decimales con coma. No se usa el símbolo de dólar (en este sitio es delimitador de fórmula).

TL;DR

El sensor de potencia de una GPU no devuelve lo que casi todo el mundo cree. La documentación de NVML es explícita: en Ampere salvo GA100 y en arquitecturas posteriores, incluida H100, nvmlDeviceGetPowerUsage devuelve potencia promediada en un intervalo de un segundo, no potencia instantánea. El estudio de la Universidad de Oxford presentado en SC24, sobre más de 70 GPU de 25 modelos contrastadas con un medidor externo de 1 mΩ, añade el dato que rompe la mayoría de las mediciones publicadas: en A100 y H100 el sensor solo muestrea el 25 % del tiempo (ventana de 25 ms dentro de un periodo de 101 ms), de modo que durante el 75 % restante la GPU puede estar consumiendo algo radicalmente distinto. Integrando potencia de forma ingenua sobre nueve benchmarks reales, el error medio fue del 39,27 %; aplicando la buena práctica bajó al 4,89 %. La consecuencia operativa es corta: si el hardware es Volta o posterior, se usa el contador acumulado de energía (nvmlDeviceGetTotalEnergyConsumption, en milijulios) y se resta, en lugar de integrar muestras. Y el contador de la GPU no es la factura: en nodos medidos de 8× H100, el nodo llega a 8,4 kW frente a 5,6 kW de suma de TDP de las GPU, el idle del nodo es de 1,8 kW, y con un PUE medio de 1,54 los vatios de NVML son del orden del 43 % de los vatios que factura la eléctrica.


Qué expone el hardware

NVML: potencia

nvmlDeviceGetPowerUsage(device, unsigned int* power) devuelve milivatios de la GPU y su circuitería asociada, por ejemplo la memoria. Está soportada desde Fermi. La nota de la documentación oficial es la parte que casi nunca se cita (NVML Device Queries):

En Fermi y Kepler la lectura tiene una exactitud del ±5 % del consumo actual. En Ampere (salvo GA100) o posteriores, la API devuelve potencia promediada sobre un intervalo de 1 s. En GA100 y arquitecturas anteriores se devuelve potencia instantánea.

Es decir, en una H100 o una L40S la llamada habitual ya entrega una media móvil de un segundo. Quien la muestrea a 1 Hz y la integra está aplicando un segundo filtro sobre una señal ya filtrada, sin declararlo. Para desambiguar, NVML expone dos campos separados accesibles vía nvmlDeviceGetFieldValues: NVML_FI_DEV_POWER_AVERAGE y NVML_FI_DEV_POWER_INSTANT, con equivalentes en línea de comandos (nvidia-smi --query-gpu=power.draw.average y power.draw.instant).

NVML: el contador de energía

nvmlDeviceGetTotalEnergyConsumption devuelve energía acumulada en milijulios desde la última recarga del driver, soportada en Volta o posterior. Es un entero de 64 bits, así que el desbordamiento no es un problema práctico: una H100 a 700 W consume del orden de 6,1·10¹⁰ J al año frente a un rango del contador de 1,8·10¹⁶ J. El único evento que lo reinicia es la recarga del driver o el reinicio del sistema, y eso sí hay que vigilarlo: un modprobe -r nvidia a mitad de una campaña produce un delta negativo.

NVIDIA no documenta la resolución temporal interna del contador, su mecanismo de acumulación ni su exactitud, y no he localizado ningún trabajo revisado por pares que lo valide contra un medidor externo. La recomendación de usarlo, que se sostiene en el resto de esta ficha, es un argumento estructural (elimina el error de cuadratura y el de aliasing), no una validación empírica publicada.

NVML: límites de potencia

FunciónUnidadSemántica
nvmlDeviceGetPowerManagementLimitmWLímite actual configurado
nvmlDeviceGetPowerManagementLimitConstraintsmWRango legal mínimo y máximo
nvmlDeviceGetPowerManagementDefaultLimitmWLímite con el que arranca la tarjeta
nvmlDeviceGetEnforcedPowerLimitmWLímite efectivo tras considerar todos los limitadores, incluida la interfaz fuera de banda

Para una medición auditable hay que registrar EnforcedPowerLimit, no PowerManagementLimit: el BMC puede estar imponiendo un tope fuera de banda que el segundo no refleja, y dos nodos aparentemente idénticos pueden estar operando a topes distintos.

DCGM: los campos equivalentes

CampoIDUnidadTipo en el exporter
DCGM_FI_DEV_POWER_USAGE155vatios en coma flotantegauge
DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION156milijulios acumuladoscounter
DCGM_FI_DEV_POWER_MGMT_LIMIT160mWgauge
DCGM_FI_DEV_ENFORCED_POWER_LIMIT164mWgauge

Ambos campos de energía están activos en el fichero de contadores por defecto de dcgm-exporter. La conclusión de auditoría es directa: un panel estándar de Prometheus que integre DCGM_FI_DEV_POWER_USAGE está integrando un gauge que ya es una media de un segundo del sensor, muestreado al collect-interval del exporter. Para energía por token la métrica correcta es el contador 156 con increase().

La precisión real del sensor

La referencia es el trabajo de Yang, Adámek y Armour (Universidad de Oxford), publicado en SC24 y disponible como arXiv:2312.02741: más de 70 GPU de 25 modelos, incluidas 10 H100 y 10 A100, contrastadas contra un medidor externo con shunt de 1 mΩ, ADC de 12 bits y muestreo interno de 34 kHz.

Periodo de actualización y ventana de promediado

GPU o arquitecturaPeriodo de actualizaciónVentana de promediado
Kepler y Maxwell10-50 Hzcrecimiento logarítmico, ~200 ms
Volta (V100)20 ms10 ms
Turing100 ms100 ms
A100 (GA100)101 ms25 ms
Ampere no-GA100 y Ada, driver anterior a marzo de 2023100 ms1 s
Ampere no-GA100 y Ada, driver 530100 ms100 ms
H100100 ms25 ms con .instant; 1 s con .average y con el campo por defecto
GH200100 ms20 ms en GPU, 10 ms en la CPU Grace

Dos lecturas de esta tabla. La primera: el driver es una variable experimental, no un detalle de infraestructura; la ventana de promediado cambió entre drivers anteriores al 530, el 530 y los posteriores. Cualquier medición que se quiera comparar en el tiempo tiene que registrar la versión del driver.

La segunda es el hallazgo central del paper. En A100 y H100 la ventana de 25 ms vive dentro de un periodo de 101 ms, lo que da un ciclo de trabajo del sensor cercano al 25 %. El sensor no promedia el intervalo completo: promedia una cuarta parte y el sistema presenta el resultado como si describiera el intervalo entero. En palabras del propio trabajo, durante el otro 75 % del tiempo la GPU puede estar consumiendo una potencia radicalmente distinta y nvidia-smi no se entera.

El error en régimen estacionario

El paper cuantifica también la afirmación de la documentación de nvidia-smi, que hablaba de una exactitud de ±5 vatios: el error real es proporcional, ±5 %, no absoluto. En una GPU capaz de consumir 700 W eso son ±35 W de sobre o infraestimación. Los errores medidos frente al medidor externo, tras corregir el desfase temporal, fueron de −4,70 % y −4,53 % en RTX 3090 y −5,43 % en A100, y el residuo se atribuye a la tolerancia del shunt físico de la propia tarjeta, que ningún software puede corregir.

La respuesta a transitorios

Se observaron cuatro comportamientos distintos en el tiempo de subida del 10 % al 90 %: subida real casi instantánea con la lectura siguiendo en el tick siguiente y un retardo de 0 a 100 ms; subida real de varios cientos de milisegundos con la lectura actualizándose igualmente al tick siguiente; crecimiento lineal a lo largo de un segundo, que corresponde a power.draw.average; y crecimiento logarítmico en 200 ms, solo en Kepler y Maxwell. De ahí sale la advertencia más incómoda del trabajo: al ejecutar un programa corto, la potencia medida corresponde probablemente a la actividad anterior al programa.

Las dos formas de obtener energía

La primera es integrar muestras, que es lo que hace cualquier poller:

$$E \approx \sum_i P(t_i) \cdot \Delta t$$

Acumula cinco fuentes de error: el ±5 % del sensor, el error de cuadratura por paso finito, el aliasing si la carga tiene contenido espectral por encima de la mitad de la frecuencia de muestreo, el hecho de que cada muestra ya es una media móvil, y el jitter de 0 a 100 ms entre el reloj del host y el tick del sensor.

La segunda es restar el contador:

$$E = \text{counter}(t_1) - \text{counter}(t_0)$$

Dos llamadas y una resta. Elimina la cuadratura, el aliasing y la desalineación temporal; el ±5 % del sensor permanece.

Por qué 1 Hz destruye una inferencia de 200 ms

Con un muestreo a 1 Hz, el teorema de muestreo solo permite reconstruir componentes por debajo de 0,5 Hz, mientras que un pulso de 200 ms tiene contenido en el entorno de 5 Hz. El número esperado de muestras dentro del evento es de 0,2, así que con probabilidad 0,8 no cae ninguna: el resultado no es ruidoso, es indefinido.

Aunque se muestree al máximo útil, que en A100 y H100 son unos 10 Hz dado el periodo de 101 ms, una inferencia de 200 ms produce una o dos actualizaciones distintas del sensor, y cada una solo ha observado 25 ms de actividad real: la cobertura efectiva ronda el 25 % del evento. Y por el cuarto comportamiento transitorio de la sección anterior, la lectura obtenida durante esos 200 ms puede describir lo que ocurría antes de lanzar el kernel.

Aliasing con cargas periódicas

El paper observa directamente el fenómeno en A100: con una onda cuadrada de periodo ligeramente distinto de 100 ms, la lectura fluctúa entre valores altos y bajos con un batido claro. Un servidor de inferencia con llegadas periódicas, comprobaciones de salud, lotes de tamaño fijo o un tick de planificador, es exactamente ese caso patológico.

Cuando solo hay potencia instantánea disponible, la buena práctica medida del trabajo de Oxford consiste en ejecutar 32 iteraciones consecutivas o un mínimo de 5 s, insertar 8 retardos controlados equiespaciados si la ventana es menor que el periodo, repetir en 4 ensayos separados con retardo aleatorio entre ellos, y desplazar la serie en post-proceso para sincronizarla con la actividad real. Sobre nueve benchmarks reales eso llevó el error medio del 39,27 % al 4,89 %.

Qué no mide el contador de la GPU

El alcance declarado por NVML es la GPU y su circuitería asociada: el módulo SXM o la tarjeta PCIe, con su HBM y sus reguladores. Quedan fuera CPU, DRAM del host, NVSwitch, tarjetas de red, almacenamiento, ventiladores y pérdidas de la fuente.

Los datos de nodo medidos vienen del trabajo de calibración empírica sobre nodos de 8× H100 SXM5 (arXiv:2506.14551):

MagnitudValor
TDP nominal del nodo declarado por el fabricante10,2 kW
Potencia del nodo en reposo1,8 kW
Máximo medido con carga de saturación8,4 kW
Pico en cargas productivas realesnunca por encima del 76 % del TDP

De ahí salen dos factores de conversión, ambos estimaciones propias a partir de esas cifras: 8 × 700 W son 5,6 kW de GPU frente a 8,4 kW de nodo, es decir, la GPU es del orden del 67 % de la potencia del nodo y el factor GPU a nodo ronda 1,50×. Encadenando con un PUE medio del sector de 1,54 (Uptime Institute, 2025), el factor total desde el contador de NVML hasta la acometida ronda 2,3×; con un PUE de 1,1 baja a 1,65×. Dicho en la forma que interesa a quien firma la factura: los vatios que reporta NVML son aproximadamente el 43 % de los vatios que factura la eléctrica en un centro de datos medio.

Como referencia complementaria, una fuente 80 PLUS Titanium a 230 V rinde un 96 % al 50 % de carga y un 91 % al 100 %, de modo que las pérdidas de alimentación añaden entre un 4 % y un 9 % que ya está incluido si se mide en la toma de corriente.

El impuesto de tener un contexto abierto

Una GPU que sirve un modelo cargado y sin tráfico no consume su idle nominal. Las mediciones sobre 335 267 muestras de producción de 14 H100 durante 18 días, más experimentos controlados, dan esta tabla:

GPUIdle sin contexto CUDAIdle con contexto CUDASobrecostePorcentaje del TDP
H10071,8 W121,7 W+49,9 W7,1 %
A10053,7 W80,0 W+26,3 W8,8 %
L40S35,6 W102,1 W+66,4 W19,0 %

El resultado que cambia el modelo mental: más del 98 % de ese sobrecoste lo produce el contexto CUDA abierto, con independencia de la memoria ocupada. Variar la VRAM asignada entre 0 y 72 GB mueve la potencia menos de 1 W. El tamaño del modelo cargado no cuesta vatios; tener el contexto abierto sí.

Ese coste no es marginal en una plataforma real. Sobre 11 791 trabajos de larga duración, el reparto medido fue de un 24 % del tiempo y un 7 % de la energía en reposo profundo, un 15 % del tiempo y un 10 % de la energía en reposo con contexto activo, y un 61 % del tiempo con un 83 % de la energía en ejecución. En cargas de serving, las GPU llegan a gastar el 48 % de la energía en periodos de baja actividad.

CPU y DRAM: RAPL

Para cerrar el nodo hace falta medir lo que no es GPU, y ahí la interfaz es RAPL a través del framework powercap del kernel, en /sys/devices/virtual/powercap/intel-rapl/. La jerarquía expone intel-rapl:N como socket y intel-rapl:N:M como subzonas (core, uncore, dram), más el dominio psys para el SoC completo desde Skylake.

FicheroContenido
energy_ujContador de energía en microjulios; escribir “0” lo reinicia si el contador lo admite
max_energy_range_ujRango del contador, es decir, el punto de desbordamiento
power_uwPotencia actual en microvatios
nameNombre de la zona

Las cifras que condicionan su uso vienen de la caracterización publicada en ACM TOMPECS: actualización cada 1 ms aproximadamente, con jitter; cuanto de energía de 61 µJ en Haswell y Skylake, 15,3 µJ en Sandy Bridge; y desbordamiento del contador en 52 minutos en un Haswell a 84 W, lo que obliga a sondear con periodo muy inferior y a detectar el salto con max_energy_range_uj. Hay además una deriva térmica que rara vez se declara: la potencia de paquete para la misma carga crece entre un 10 % y un 12 % entre 37 °C y 74 °C en Haswell. El calentamiento previo no es opcional.

El acceso, en cambio, ya no es libre. El ataque PLATYPUS (IEEE S&P 2021) demostró un canal lateral por consumo puramente software a través de RAPL, con CVE-2020-8694 y CVE-2020-8695 asociados. La mitigación en Linux llegó en el commit 949dd0104c49, incluido en 5.10.0-rc4, que cambió los permisos por defecto para que solo root pueda leer energy_uj. La mitigación de microcódigo asociada a SGX va más lejos e introduce ruido aleatorio en la energía reportada y cambia la frecuencia de reporte: en máquinas con esa mitigación activa, las lecturas RAPL están deliberadamente degradadas. Medir CPU y DRAM desde un contenedor exige por tanto root, una regla udev que relaje los permisos, o un demonio privilegiado.

El stack de software

HerramientaFuente del dato de GPUContador o potenciaIntervalo por defectoSobrecoste
ZeusNVMLContador si la arquitectura es Volta o posterior; polling en caso contrarioDelimitado por ventanasMenos de 10 ms por llamada
dcgm-exporterNVML vía DCGMAmbos campos disponiblescollect-interval = 30 000 ms5-10 W adicionales en la lectura IPMI del servidor
CodeCarbonNVMLContadormeasure_power_secs = 15 s5,38 % a 46,75 % de tiempo a 1 kHz
ScaphandreSin soporte de GPU; RAPLContador energy_ujNo documentado3,81 % a 28,38 % a 1 kHz
KeplerRAPL e IPMI; GPU solo como opción experimentalContadores hardware

Cuatro precisiones que cambian la elección:

  • Zeus decide en código con nvmlDeviceGetArchitecture y usa el contador acumulado en Volta o posterior, con una API de ventanas anidables (begin_window / end_window). Es la implementación de referencia de lo que recomienda esta ficha. Su paper de NSDI'23 documenta además reducciones de energía del 23,8 % al 75,7 % eligiendo bien tamaño de lote y límite de potencia, y del 3,0 % al 31,5 % tocando solo el límite, con cinco segundos de perfilado por punto suficientes para resultados estables.
  • Kepler ha eliminado eBPF. El anuncio de la CNCF de junio de 2026 describe el rediseño hacia lectura de /proc y /sys, motivado por los permisos CAP_BPF y CAP_SYSADMIN que bloqueaban despliegues, y por la pérdida de procesos de vida corta que infravaloraba la huella. La nueva métrica de nodo sigue de cerca el patrón de IPMI y elimina los picos espurios de varios kilovatios de la versión anterior. Para GPU sigue siendo una opción experimental: hoy no es la herramienta para medir Wh/token en acelerador.
  • CodeCarbon usa el contador de NVML, lo cual es correcto, pero su estimación de RAM es una heurística de 5 W por módulo a partir del total de gigabytes, y el propio código lo reconoce.
  • DCGM entrega métricas de perfilado a 1 Hz por defecto, y advierte de que recolectar a frecuencias mayores devuelve ceros porque agrupa métricas internamente.

Sobre el coste de medir, el estudio empírico de sobrecoste de herramientas basadas en RAPL, todas forzadas a 1 kHz, deja una ordenación clara:

HerramientaSobrecoste temporal
CodeCarbon5,38 % a 46,75 %
Scaphandre3,81 % a 28,38 %
Turbostat2,73 % a 14,37 %
PowerJoular1,67 % a 8,88 %
perf−1,00 % a 4,26 %
Lectura RAPL en espacio de usuario−0,70 % a 2,93 %
Lectura RAPL en espacio de núcleo−0,17 % a 0,99 %

La diferencia de fondo es que una llamada al sistema cuesta del orden de 1,36·10⁻³ ms y una instrucción rdmsr entre 2,3·10⁻⁴ y 5,6·10⁻⁴ ms, un orden de magnitud menos. La recomendación de los autores es ajustar la granularidad al fenómeno que se mide en lugar de subir la frecuencia por defecto, y limitar el sondeo a los dominios necesarios.

Procedimiento para que un Wh/token sea auditable

  1. Fijar y registrar el estado eléctrico. Anotar EnforcedPowerLimit, el límite por defecto y la versión del driver; bloquear relojes o el tope de potencia si el experimento lo requiere.
  2. Calentar hasta temperatura estable. La deriva térmica de RAPL es de un 10 % a un 12 % entre 37 °C y 74 °C, y el transitorio de power.draw.average tarda hasta un segundo en estabilizarse.
  3. Medir la línea base en reposo dos veces: sin contexto CUDA y con el modelo cargado. Son dos valores distintos y ambos hacen falta.
  4. Usar ventanas de al menos 5 s o 32 iteraciones consecutivas.
  5. Repetir en 4 ensayos separados con retardo aleatorio, y añadir 8 retardos equiespaciados si se está integrando potencia instantánea con ventana menor que el periodo.
  6. Preferir el contador acumulado siempre que la arquitectura sea Volta o posterior, vigilando recargas de driver.
  7. Reportar dispersión. El propio paper de Oxford publica media y desviación típica del error; sin intervalo, una cifra de Wh/token no es auditable.
  8. Declarar la frontera de medición: GPU, nodo o acometida. Sin esa etiqueta, dos cifras de la misma instalación difieren en un factor 2,3 sin que nadie sepa por qué.

Restar el idle, o no

No hay consenso, y lo honesto es tratarlo como una decisión declarada. En producción la GPU está aparcada con el modelo cargado, y ese consumo aparece en la factura: restarlo borra un coste real. Para comparar la eficiencia marginal de dos modelos o dos kernels, en cambio, restarlo aísla la variable. La salida practicable es reportar ambas cifras con etiqueta: Wh/token bruto, que incluye el reposo y corresponde a la factura, y Wh/token marginal, comparable entre modelos. Lo que no vale es mezclarlas en la misma tabla sin distinguirlas.

Queda una trampa de estimación que sigue viva en muchas hojas de cálculo: multiplicar TDP por tiempo sobreestima la energía real hasta 4,1 veces, según el ML.ENERGY Benchmark.

Power capping: qué cuesta y qué ahorra

ConfiguraciónAhorro de energíaPérdida de rendimiento
V100 a 200 W sobre 300 W (67 % del TDP), entrenamiento BERT~15 %apenas degradación
V100 a 200 W, conjunto de cargas10-20 %menos del 5 %
V100 a 100 W (33 % del TDP)40-60 %30-40 %
A100 a 175 W sobre 400 W (44 % del TDP), inferencia de LLaMA 65B22-24 %5-8 %

La asimetría entre fases explica el resultado. La medición de Microsoft en ASPLOS'24 documenta que la fase de prompt es breve y alcanza o supera el TDP, mientras que la fase de generación de tokens es más larga y consume menos: el tope de potencia penaliza mucho más el TTFT que el TPOT. En su clúster de producción, la inferencia usa el 79 % de la potencia de pico frente al 97 % del entrenamiento, y sus mediciones muestran que se puede recuperar hasta un 20 % de potencia con menos de un 7 % de pérdida de rendimiento.

El barrido publicado de 200 W a 700 W en H100 y H200 añade el matiz de dónde está la rodilla: entre 500 W y 700 W cada escalón de 100 W solo aporta alrededor de un 10 % de rendimiento en cargas limitadas por cómputo, mientras que la H100 mantiene el ancho de banda de memoria pico incluso a 200 W. Las zonas de operación más estables identificadas son 400 W en H100 y 500 W en H200. Que el subsistema de memoria se proteja frente al recorte es la razón física por la que la fase de decodificación de un LLM, limitada por memoria, pierde tan poco rendimiento bajo un tope de potencia.

Dos avisos sobre esas cifras: el barrido H100/H200 se midió con nvidia-smi sondeando cada 10 s, exactamente el problema descrito arriba, aunque en cargas largas y estacionarias el sesgo se promedia; y no existe publicación que aísle el efecto del bloqueo de reloj de memoria sobre Wh/token en inferencia LLM, de modo que cualquier cifra concreta en ese punto tiene que ser medida propia.

Cifras de referencia

GPUTDP
H100 SXMhasta 700 W, configurable
H100 NVL350-400 W, configurable
A100 80GB SXM400 W
A100 80GB PCIe300 W
RTX 5090575 W, con 1000 W de fuente recomendada
Nodo HGX de 8× H100 SXM10,2 kW nominales

Potencia media medida en inferencia: en un clúster HPC con vLLM sobre A100, la potencia agregada del conjunto de GPU osciló entre 999 W y 2 983 W según modelo y paralelismo, con Nemotron 70B sobre 8× A100 en 2 983 W, unos 373 W por GPU, alrededor del 93 % del TDP.

Energía por token, con la advertencia de que las cifras publicadas miden fronteras distintas y no se pueden mezclar sin normalizar por número de GPU y tokens de salida:

FuenteCifra
TokenPowerBench, H100 94 GB40 J por token en carga estándar, más de 60 J por token en configuraciones de alto throughput
TokenPowerBench, efecto del lote−25 % de energía por token entre lote 32 y 256 en un modelo de 70B
TokenPowerBench, efecto del contextode 2K a 10K tokens de contexto en Llama 3 70B: energía ×3
TokenPowerBench, efecto de la cuantizaciónLlama 3 405B en FP8 frente a FP16: −30 % de energía por token
TokenPowerBench, efecto del motorTensorRT-LLM y vLLM frente al motor de Transformers: −25 % a −40 %
Medición por prompt en lote0,0074-0,0289 Wh en modelos de 7B a 14B; 0,0835-0,6912 Wh en 70B a 405B; peticiones sueltas sin lote, entre 10 y 100 veces más caras

Ver también

Fuentes