GPU idle: el coste que no aparece en ninguna factura pero lo paga todo el TCO

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 coste por token de inferencia on-premise es ( \text{€/GPU-hora} \div \text{throughput} ). El throughput es función directa de la ocupación útil de la GPU. En un nodo genérico de 4×H100 SXM con un coste de referencia de ~11 € por GPU-hora (amortización + energía + infraestructura), la curva de coste sobre ocupación tiene este aspecto:

Ocupación útilThroughput efectivo (tok/s)Coste por 1M tokens
20 %~700~43 €
40 %~1 400~21 €
70 %~2 500~12 €
85 % (techo práctico)~3 000~10 €

Doblar la ocupación del 20 % al 40 % recorta el coste por token a la mitad, sin comprar más hierro. A 70 % el coste compite con proveedores cloud europeos (~2,2 €/GPU-hora en on-demand Scaleway). A 20 % on-prem es cuatro veces más caro que alquilar. La palanca no es el modelo, ni la precisión: es cuántas GPU-horas de pago producen tokens de servicio.


1 · La identidad fundamental

El coste por millón de tokens (CPM) en inferencia propia no es un precio de lista. Es:

$$ \text{CPM} = \frac{C_{\text{GPU}} \cdot N_{\text{GPU}}}{T_{\text{ef}} \times 3600 / 10^6} $$

donde ( C_{\text{GPU}} ) es el coste por GPU-hora (€/h), ( N_{\text{GPU}} ) el número de GPUs asignadas al servicio y ( T_{\text{ef}} ) el throughput efectivo (tok/s). Despejando la dependencia con la ocupación:

$$ T_{\text{ef}} = T_{\text{pico}} \times \rho $$

siendo ( \rho \in [0,1] ) la tasa de ocupación útil (fracción del tiempo en que la GPU está procesando tokens de servicio real). La identidad resultante:

$$ \text{CPM} = \frac{C_{\text{GPU}} \cdot N_{\text{GPU}}}{T_{\text{pico}} \times \rho \times 3600 / 10^6} $$

La consecuencia directa: CPM es inversamente proporcional a ( \rho ). Duplicar ( \rho ) divide CPM por dos. El numerador (coste del hierro) no cambia.

Los posts coste-por-token-y-por-request y capacity-planning-inferencia-llm-on-premise cubren cómo calcular ( C_{\text{GPU}} ) y el throughput pico de referencia. Este artículo se ocupa de ( \rho ): cómo medirlo, qué lo limita y cómo subirlo.


2 · Por qué la métrica estándar engaña: DCGM_FI_DEV_GPU_UTIL

El campo DCGM_FI_DEV_GPU_UTIL (field ID 203) aparece en nvidia-smi como «GPU-Util». Su definición oficial en la documentación DCGM:

«GPU Utilization» — porcentaje de tiempo durante el que uno o más kernels estaban ejecutándose en la GPU.

El problema para inferencia LLM: la fase de decode es memory-bound. La GPU corre un kernel de lectura de pesos desde HBM token a token; por tanto GPU_UTIL registra cercano al 100 % aunque los tensor cores estén al 15 % de su capacidad. El campo mide actividad, no trabajo útil.

La distinción es crítica para FinOps: un operador que ve GPU_UTIL 98 % asume «GPU saturada, no cabe más carga». La realidad puede ser «los tensor cores están al 20 % y el cuello de botella es la HBM», lo que abre espacio a continuous batching o bin-packing adicional.

Las métricas que miden ocupación real son las del subsistema _FI_PROF_*, disponibles en DCGM 3.x con el módulo de profiling activado:

Tabla de campos DCGM relevantes

Campo DCGMField IDQué mideUnidadNota operativa
DCGM_FI_DEV_GPU_UTIL203% tiempo con ≥1 kernel activo%Engañoso en decode LLM
DCGM_FI_PROF_SM_ACTIVE1002Ratio de ciclos con ≥1 warp activo por SM0–1Actividad de compute, no ocupación
DCGM_FI_PROF_SM_OCCUPANCY1003Warps residentes / máximo teórico por SM0–1Paralelismo intra-SM real
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE1004% ciclos con tensor cores (HMMA) activos0–1La métrica de eficiencia compute real
DCGM_FI_PROF_DRAM_ACTIVE1005% ciclos con HBM transfiriendo0–1Saturación de memoria
DCGM_FI_PROF_PCIE_TX_BYTESBytes TX por PCIebytes/sÚtil en inferencia PCIe
DCGM_FI_DEV_FB_USED252HBM usadaMiBPresupuesto VRAM
DCGM_FI_DEV_POWER_USAGEConsumo realWPara coste energético real
DCGM_FI_DEV_CLOCK_THROTTLE_REASONSBitmap causas de throttlebitmapDetecta degradación silenciosa

Los campos _PROF_* requieren el módulo de profiling de DCGM y permisos adecuados del driver. Se documentan exhaustivamente en la referencia de field IDs de NVIDIA DCGM y en el post hermano observabilidad-gpu-dcgm-llm.

Lectura característica en decode LLM (Llama 70B FP8 en H100)

MétricaValor típico decodeInterpretación
DCGM_FI_DEV_GPU_UTIL95–99 %Mentira: hay kernel activo
DCGM_FI_PROF_SM_ACTIVE0,45–0,65SMs con warps el 45–65 % del tiempo
DCGM_FI_PROF_SM_OCCUPANCY0,30–0,55Warps residentes al 30–55 % del máximo
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE0,10–0,25Tensor cores activos solo el 10–25 %
DCGM_FI_PROF_DRAM_ACTIVE0,75–0,90HBM ocupada el 75–90 % del tiempo

Lectura: el decode es memory-bound. Los pesos del modelo se cargan desde HBM para cada token; la HBM está saturada pero los tensor cores esperan. Toda la «utilización» del nvidia-smi viene de lecturas de memoria, no de cómputo.


3 · MFU y HFU: la ocupación expresada en FLOPs

La métrica canónica de eficiencia de cómputo es el MFU (Model FLOPs Utilization), definida en el paper PaLM (Chowdhery et al., arXiv 2204.02311, sección 4):

$$ \text{MFU} = \frac{T_{\text{obs}} \times C_{\text{modelo}}}{P_{\text{pico}}} $$

donde ( T_{\text{obs}} ) es el throughput observado (tok/s) y ( P_{\text{pico}} ) es el rendimiento teórico pico del hardware (FLOP/s).

donde ( C_{\text{modelo}} ) es el número de FLOPs por token en un forward pass completo. Para un transformer denso con ( P ) parámetros, la aproximación habitual (forward + backward = 6P FLOPs por token; solo forward = 2P):

$$ C_{\text{modelo}} \approx 2P \quad \text{(inferencia)} $$

$$ C_{\text{modelo}} \approx 6P \quad \text{(entrenamiento, forward + backward)} $$

El HFU (Hardware FLOPs Utilization) mide los FLOPs realmente ejecutados en hardware, incluyendo recomputación de activaciones (gradient checkpointing). En entrenamiento con recompute:

$$ C_{\text{hardware}} \approx 8P \quad \text{(forward × 2 + backward × 4)} $$

por tanto HFU > MFU cuando hay recompute; son idénticos sin recompute.

Valores típicos de MFU

RégimenHardwareMFU típicoRégimen limitante
Entrenamiento (large batch, BF16)H100 SXM35–50 %compute-bound
Entrenamiento PaLM 540BTPU v446,2 %compute-bound
Inferencia prefill (batch grande)H100 SXM25–45 %compute-bound
Inferencia decode (bs=1)H100 SXM3–8 %memory-bound
Inferencia decode (continuous batching, bs=32–64)H100 SXM15–30 %memory-bound atenuado

El decode con batch size 1 tiene MFU de un solo dígito porque el hardware pasa la mayor parte del tiempo esperando que la HBM entregue pesos. Subir el batch size (continuous batching) amortiza la lectura de pesos entre más tokens simultáneos y sube el MFU.

El modelo Roofline

El roofline sitúa cada operación en el espacio (intensidad aritmética, throughput):

Roofline: prefill vs decode en H100 SXMIntensidad aritmética (FLOP/byte)Throughput (TFLOP/s)~990 TFLOP/s (H100 BF16)banda mem.~3,35 TB/s~295FLOP/byte ridgedecode bs=1MFU ~5 %decode bs=32MFU ~20 %prefill bs=128MFU ~38 %

El decode con batch size 1 cae en la zona memory-bound de la izquierda del ridge point. Subir el batch (continuous batching) desplaza el punto hacia la derecha y arriba, acercándolo al roofline.


4 · Sensibilidad del TCO a la ocupación

Hipótesis del ejemplo: nodo genérico con 4×H100 SXM 80 GB, coste total del nodo ~44 €/hora (amortización 5 años de ~220 000 €, energía ~4×700 W a ~0,12 €/kWh, más infraestructura rack/colocation; ver coste-por-token-y-por-request para el detalle de la identidad). Throughput pico de referencia con Llama 70B FP8 + continuous batching: ~3 500 tok/s agregado.

$$ \text{CPM}(\rho) = \frac{44\ \text{€/h}}{3500 \times \rho \times 3600 / 10^6} = \frac{44 \times 10^6}{3500 \times \rho \times 3600} = \frac{3{,}49}{\rho}\ \text{€/1M tok} $$

Ocupación ( \rho )Throughput efectivo (tok/s)CPM on-premCPM cloud on-demand (~2,2 €/GPU-h)
20 %700~17,5 €~6,3 €
40 %1 400~8,7 €~6,3 €
60 %2 100~5,8 €~6,3 €
70 %2 450~5,0 €~6,3 €
85 %2 975~4,1 €~6,3 €

Punto de cruce on-prem / cloud: con este hardware de ejemplo, la ventaja de coste on-prem respecto a un cloud europeo comparable empieza en ( \rho \approx 55\text{-}60,% ). Por debajo, el idle convierte on-prem en más caro que alquilar. La columna de cloud es fija porque el cloud factura por hora usada, no por el throughput que se extrae de ella.

Cifras de ejemplo con hardware genérico. Los números reales dependen del precio de la amortización, el coste de la energía local, el modelo y la precisión. La estructura de la curva —CPM inversamente proporcional a ( \rho )— es universal.


5 · Métricas de idle: dónde se pierde la ocupación

Antes de aplicar palancas, hay que saber qué tipo de idle domina. Tres categorías:

Tipo de idleSíntoma en métricasCausa habitual
Idle schedulingDCGM_FI_DEV_POWER_USAGE bajo, SM_ACTIVE < 0,05No hay requests en cola; GPU esperando trabajo
Idle batchingSM_ACTIVE alto, PIPE_TENSOR_ACTIVE bajo, DRAM_ACTIVE bajoBatch demasiado pequeño; prefill stall entre requests
Idle memory-boundDRAM_ACTIVE alto, PIPE_TENSOR_ACTIVE bajoDecode normal; HBM es el cuello; batch size insuficiente

El idle de scheduling es el más caro y el más directo de atacar con bin-packing. El idle de batching se ataca con continuous batching. El idle memory-bound en decode no desaparece del todo (es la física del transformador), pero se atenúa con batch size mayor.

Las métricas de motor de inferencia que complementan el diagnóstico desde el lado del servicio se documentan en observabilidad-gpu-dcgm-llm y anatomia-metricas-dcgm-vllm-anomalias.


6 · Palancas para subir la ocupación

Tabla palanca × efecto × cuándo aplica

PalancaEfecto sobre ( \rho )Cuándo aplicaComplejidad operativa
Continuous batchingAlto: elimina idle entre requests; sube MFU decode de ~5 % a ~20 %Siempre en inferencia; activado por defecto en vLLMBaja (parámetro del motor)
Bin-packing del scheduler (kube-scheduler / Kueue)Alto: concentra cargas en menos nodos; libera nodos completos para apagado o rebalanceoClusters con carga variable a lo largo del díaMedia (política de scheduler)
MIG (Multi-Instance GPU)Medio: llena GPUs con cargas ligeras que antes vivían solas en una GPU enteraCargas heterogéneas: embeddings + reranker + guardrail + modelo grandeAlta (reparticionado en caliente no disponible)
Time-slicingBajo-medio: sube ocupación en dev/ráfagas; sin aislamientoGPUs de consumo (RTX 5090/4090); dev multi-tenant de bajo riesgoBaja
MPSMedio: ejecución concurrente de múltiples procesos pequeños; reduce overhead context-switchMuchos kernels pequeños concurrentes en GPU datacenter; confianza entre cargasMedia
Chunked prefillMedio: intercala prefill y decode; reduce TTFT spike y sube throughputCargas con mix de prompts cortos y largosBaja (flag vLLM)
Quantización (FP16→FP8→INT4)Indirecto: sube throughput pico, lo que baja CPM para la misma ( \rho )Modelos con soporte de kernels quantizados (Hopper FP8 nativo)Media
Autoscaling (KEDA)Mantiene ( \rho ) alta escalando réplicas según colaCarga variable y predecible; cluster con capacidad de spareMedia

El post compartir-gpu-time-slicing-mps-mig detalla los tres mecanismos de compartición (time-slicing, MPS, MIG) con presupuestos de VRAM.

Bin-packing con Kueue

Kueue (sigs.k8s.io/kueue) es el gestor de colas de jobs GPU nativo de Kubernetes. Su modelo de cohorts y nominal quotas permite bin-packing activo: los jobs se acumulan en cola y se lanzan solo cuando hay un nodo que puede recibirlos completo, en lugar de fragmentar la carga entre nodos parcialmente ocupados.

El BestFit packing en el ClusterQueue se configura con:

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: gpu-prod
spec:
  preemption:
    reclaimWithinCohort: Any
    withinClusterQueue: LowerPriority
  resourceGroups:
    - coveredResources: ["nvidia.com/gpu"]
      flavors:
        - name: h100-sxm
          resources:
            - name: "nvidia.com/gpu"
              nominalQuota: 16

La política de preemption por prioridad asegura que los jobs de producción desplacen los de experimentación cuando hay escasez, manteniendo la ocupación de los nodos de producción alta.

MIG como palanca de bin-packing dentro de la GPU

MIG permite rellenar una H100 con cargas ligeras que de otro modo vivirían solas en una GPU entera. Un perfil 3×2g.20gb + 1×1g.10gb en una H100 puede alojar simultáneamente un modelo 7B FP8 (~14 GB de pesos), dos servicios de embeddings y un guardrail INT4, todos con aislamiento de hardware. Sin MIG, cada una de esas cargas ocuparía una GPU completa con ( \rho ) individual < 10 %.

Los perfiles disponibles en H100 80 GB (SXM5) según la MIG User Guide de NVIDIA:

Perfil MIGCompute slicesMemoriaMáx. instancias
1g.10gb1/7 SMs10 GB7
1g.20gb1/7 SMs20 GB4
2g.20gb2/7 SMs20 GB3
3g.40gb3/7 SMs40 GB2
4g.40gb4/7 SMs40 GB1
7g.80gb7/7 SMs80 GB1 (GPU entera)

MIG no está disponible en GPUs de consumo (RTX 5090, RTX 4090). Time-slicing es la única opción de compartición en ese hardware.


7 · Continuous batching: el efecto sobre MFU

El continuous batching (también llamado iteration-level scheduling o in-flight batching) es el mecanismo que mayor impacto tiene sobre la ocupación en inferencia. La idea: en lugar de esperar a que un batch completo termine para lanzar el siguiente, el motor evalúa el pipeline tras cada token y sustituye las secuencias completadas por nuevas requests de la cola de espera.

Efecto cuantificado en vLLM:

  • Sin batching (bs=1, estático): MFU decode ~3–8 %; GPU en idle entre requests.
  • Con continuous batching (bs dinámico 16–64): MFU decode ~15–30 %; la GPU casi nunca espera.
  • En cargas de prefill puro con batch grande (bs=128+): MFU prefill 25–45 %; se acerca al roofline compute.

El parámetro --max-num-seqs de vLLM controla el número máximo de secuencias en el batch concurrente. Aumentarlo sube la ocupación hasta que la HBM se convierte en cuello de botella (ver DCGM_FI_PROF_DRAM_ACTIVE > 90 % sostenido).


8 · Hardware y escala: qué aplica a qué

HardwareMIGMPSTime-slicingContinuous batchingNota
H100 SXM / H200Sí (7 inst.)Referencia on-prem datacenter
A100 SXM/PCIeSí (7 inst.)Generación anterior; HBM2e
L40S / L40NoAda Lovelace; sin MIG; buen precio/VRAM
RTX 5090NoSí (limitado)Consumo; sin MIG; no escala en producción
RTX 4090NoSí (limitado)Consumo; 24 GB VRAM; sin MIG

La RTX 5090 y RTX 4090 ilustran el caso de hardware que no escala para multi-tenant con aislamiento: no soportan MIG, la VRAM es escasa para modelos > 7B con KV-cache amplio, y el TDP (600 W / 450 W) es alto respecto al throughput. Para inferencia en producción a escala, el 4×H100 SXM es el nodo de referencia de esta serie.


9 · Flujo de diagnóstico FinOps: del CPM alto a la causa

CPM alto detectadoOpenCost + LiteLLMPOWER_USAGEbajo?Sí → idle schedulingBin-packing / KueueAutoscaling downNoPIPE_TENSOR_ACTIVEbajo (<0,10)?Sí → idle batchingContinuous batchingSubir --max-num-seqsNoDRAM_ACTIVE altoy batch ya grande?Sí → memory-boundQuantización FP8/INT4Modelo menor / TPNoMIG / MPS / bin-pack multi-modelo

10 · Ejemplo numérico: impacto del continuous batching sobre el CPM

Punto de partida: Llama 70B FP8 en 4×H100 SXM, carga de 8 requests/s con 512 tokens de salida media, sin continuous batching (bs estático = 8):

  • Throughput observado: ~900 tok/s (decode dominante).
  • ( \rho_{\text{efectiva}} \approx 900 / 3500 \approx 0{,}26 ).
  • CPM: ( 44 / (900 \times 3600 / 10^6) \approx 13{,}6\ \text{€} ).

Misma carga, activando continuous batching con --max-num-seqs 64:

  • Throughput observado: ~2 300 tok/s (batch dinámico rellena las gaps).
  • ( \rho_{\text{efectiva}} \approx 2300 / 3500 \approx 0{,}66 ).
  • CPM: ( 44 / (2300 \times 3600 / 10^6) \approx 5{,}3\ \text{€} ).

Reducción de CPM: del 13,6 € al 5,3 €, –61 %, sin ningún cambio de hardware y sin tocar el modelo.

Los parámetros de vLLM relevantes para subir el throughput efectivo:

--max-num-seqs 64            # batch concurrente máximo
--max-num-batched-tokens 16384  # tokens totales por iteración
--enable-chunked-prefill      # intercala prefill y decode

Ver también


Fuentes