GuideLLM a fondo: validar el SLO bajo carga y dimensionar desde el codo

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).

Qué cubre este artículo

Tercer artículo del track de benchmarking (B3). En B2 se vio el catálogo de herramientas; aquí entramos en GuideLLM a fondo, porque es la que responde la pregunta operativa que dimensiona una plataforma: ¿hasta dónde puedo cargar este motor sin romper el SLO? No es un benchmark de catálogo (“X tokens/s”), es un sweep dirigido por SLO que encuentra el codo —la capacidad segura— y del que sale el número de réplicas y el coste por token reales. Veremos sus modos de carga, cómo se define el SLO, cómo se lee la salida y cómo se convierte el codo en sizing y en euros. Con comandos reales; sin recomendaciones, solo la mecánica.


Por qué GuideLLM

GuideLLM (proyecto vLLM) genera patrones de tráfico realistas y configurables y captura distribuciones completas de TTFT, ITL y comportamiento de extremo a extremo, para evaluación dirigida por SLO, con sweeps reproducibles que identifican el rango de operación seguro (Red Hat). A diferencia de un micro-bench, mide el motor, no el cliente (carga multi-proceso), y a diferencia de MLPerf, dimensiona tu caso concreto (tu modelo, tu SLO, tu carga). Es la herramienta del medio: ni para tunear un flag, ni para comparar fabricantes, sino para decidir cuántas GPUs necesitas.


Los modos de carga (rate-types)

GuideLLM ofrece varios modos a través de --rate-type, y elegir el correcto es la mitad del trabajo:

--rate-typeQué haceCuándo
synchronousuna petición cada vezlatencia base, sin concurrencia
concurrentmantiene N peticiones simultáneas fijasmedir a una concurrencia concreta
poissonpeticiones por segundo según Poissonsimular tráfico real (llegadas aleatorias)
throughputsatura el motor al máximocapacidad bruta máxima
sweepbarrido automático de idle a máximohallar el codo (el más útil)

En modo concurrent, el --rate "1,2,4" lanza tres benchmarks a esas concurrencias (Medium · GuideLLM en OpenShift). El modo poisson es el más realista para inferencia online (las peticiones no llegan a intervalos regulares), y el sweep es el que automatiza la búsqueda del punto de saturación.


El sweep automático: de idle al codo

El modo estrella. Al correr un perfil de sweep, GuideLLM incrementa automáticamente la tasa de petición desde idle hasta el máximo throughput a lo largo de 10 rondas, y produce un informe HTML interactivo con latencia y throughput detallados (Red Hat · GuideLLM en Kubernetes). Durante el sweep, al detectar la saturación identifica la iteración anterior —el codo— y la devuelve como capacidad estimada; si no detecta saturación, hay que extender el sweep más allá del codo.

métricarondas del sweep (idle → máximo) →throughput (satura)latencia P99 (se dispara)codocapacidad estimada (segura bajo SLO)El sweep recorre 10 rondas; el codo es la última ronda antes de que el P99 rompa el SLO. Hay que extenderlo PAST el codo para verlo.

Ejemplo trabajado: leer un sweep de 10 rondas

Una salida ilustrativa de un sweep sobre un 70B en 8×H100 (SLO: TTFT P99 < 500 ms), por ronda:

RondaTasa (req/s)TTFT P99 (ms)ITL P50 (ms)Throughput (tok/s)Goodput (tok/s)
129519480480
38140201.9001.900
514240223.1003.060
617460243.4003.330
720760313.7002.520
9261.500483.950900
10302.400714.000380

Cómo se lee: hasta la ronda 6 (17 req/s) el TTFT P99 cumple el SLO (460 ms) y el goodput ≈ throughput (3.330 ≈ 3.400). En la ronda 7 el P99 ya rompe (760 ms) y el goodput cae a 2.520. El codo está entre la 6 y la 7: la capacidad segura es la de la ronda 6, ~3.330 tok/s de goodput. Las rondas 9 y 10 dan más throughput bruto (3.950, 4.000) pero con goodput de 900 y 380 —el sistema “rinde” sirviendo sobre todo peticiones que incumplen—. Quien reporte “4.000 tok/s” describe la ronda 10, donde el sistema está roto. El número defendible es 3.330 tok/s bajo TTFT P99 < 500 ms, y ese es el que entra en el sizing. Fíjate también en que el sweep tuvo que llegar hasta la ronda 10 (P99 de 2.400 ms) para ver dónde rompía: por eso hay que extenderlo más allá del codo.


Definir el SLO: el número que decide el codo

El “codo” no es absoluto: depende del SLO que definas. Un SLO típico de inferencia online:

MétricaUmbral de ejemploQué protege
TTFT P99< 500 msla espera al primer token (interactividad)
TPOT / ITL P95< 50 ms/tokenla “velocidad de tecleo” percibida
Tasa de error< 0,1 %fiabilidad

El goodput —el throughput que cumple ese SLO— es la métrica que define el codo: la capacidad segura es la máxima carga donde el goodput ≈ throughput. Cambiar el SLO mueve el codo: un SLO de TTFT más estricto (200 ms) da un codo más temprano (menos capacidad segura) que uno laxo (1 s). Por eso el SLO se fija antes del sweep, y se reporta junto al resultado: una capacidad “de 3.330 tok/s” sin decir bajo qué SLO no significa nada.


Ejecución: el comando

Un sweep dirigido por SLO contra un endpoint vLLM:

guidellm benchmark \
  --target "http://vllm:8000" \
  --rate-type sweep \
  --max-seconds 120 \
  --data "prompt_tokens=1024,output_tokens=256" \
  --output-path resultados.json

Parámetros clave: --rate-type sweep (el barrido automático), --data (la distribución de longitudes de la carga, que debe parecerse a tu tráfico real), --max-seconds (duración por ronda), --target (el endpoint). Para el SLO, GuideLLM permite definir las restricciones de latencia que marcan el goodput. La salida va a --output-path en JSON, YAML o CSV, además del informe HTML interactivo.


Definir la carga: el --data decide el resultado

El parámetro --data (la distribución de longitudes de prompt y salida) cambia el codo tanto como el SLO. Tres formas, de menos a más fiel:

Carga--dataFidelidad
Longitud fijaprompt_tokens=1024,output_tokens=256baja: el tráfico real no es fijo
Distribución sintéticarangos de longitudmedia
Trazas realesdataset de tu tráficoalta: el codo que aplica

La trampa: un sweep con prompts cortos de longitud fija da un codo optimista que no se parece a producción, donde los prompts largos dominan el coste de prefill y adelantan el codo. Para dimensionar de verdad, alimenta GuideLLM con la distribución real de tu tráfico (longitudes de prompt y salida medidas en producción). El codo de un sweep solo es tan realista como la carga que le metes; con --data irreal, dimensionas para un tráfico que no existe.

Además, el perfil de llegada importa: --rate-type poisson simula llegadas aleatorias (como el tráfico real online), mientras que concurrent mantiene N fijas (como un batch). Un servicio online medido con concurrent puede dar un codo distinto al real; para inferencia interactiva, poisson es el perfil honesto.


La salida: qué leer

GuideLLM produce reportes estandarizados y exportables para dashboards, análisis y seguimiento de regresiones, en JSON, YAML y CSV (Red Hat). Lo que importa leer, por ronda del sweep:

  • TTFT (P50, P95, P99) — la latencia al primer token.
  • ITL/TPOT (P50, P95, P99) — entre tokens.
  • Throughput (req/s y tok/s) — el bruto.
  • Goodput — el que cumple el SLO (el número honesto).

El informe HTML interactivo permite ver las distribuciones completas (no solo medias), que es donde se ve la cola de latencia que una media oculta. Para el sizing y la comparación, el JSON es lo que se versiona y se mete en el harness reproducible.


Del codo al sizing y al coste

Aquí GuideLLM se conecta con el resto de la serie. Del codo sale el número de réplicas y el coste por token:

Codo (GuideLLM)goodput por réplicaCarga objetivotok/s en pico (SLO)Nº de réplicascarga ÷ goodputCoste/token€/h ÷ goodputEl goodput del codo (no el throughput bruto) es el denominador del sizing y del coste por token.Dimensionar con el throughput bruto sobredimensiona el goodput y rompe el SLO en producción.

El cálculo: si el codo da un goodput de 3.330 tok/s por réplica y tu carga objetivo en pico es de 20.000 tok/s, necesitas 6 réplicas (20.000 ÷ 3.330 ≈ 6,0). Y el coste por token sale del coste de la réplica (de OpenCost, ~11 €/h) dividido por su goodput: ~0,92 €/1M tokens. Es el puente con el capacity planning y con el coste por token: el denominador correcto es el goodput del codo, no el throughput de catálogo. Dimensionar con el throughput bruto te deja corto de capacidad útil y rompe el SLO en producción.


Despliegue como Job de Kubernetes

GuideLLM se ejecuta como un Job de Kubernetes dentro del cluster para benchmarkear modelos servidos por la plataforma de orquestación (Red Hat). Esto es importante para la fidelidad: el generador de carga corre dentro del cluster, en la misma red que el endpoint, así que mide el motor sin la latencia de red de un cliente externo. El Job se parametriza con el target, el rate-type y la carga, y vuelca el JSON a un volumen o a un almacén para versionarlo. Correrlo como Job también facilita la automatización (CronJob para benchmarking periódico, o disparado por CI).


Validación de SLO en CI: detectar regresiones

El uso más valioso a medio plazo: un Job que corre un sweep corto en cada cambio (release de vLLM, cambio de config) y compara el goodput con la línea base. Si el goodput cae más de un umbral o el codo se adelanta, falla el pipeline. Como GuideLLM exporta JSON estandarizado para seguimiento de regresiones, comparar dos corridas es trivial. Así una regresión de rendimiento —que es una regresión de coste y de capacidad— se detecta en el commit, no en producción. No hace falta el sweep completo en cada commit: un sweep corto que cubra el codo basta para detectar la regresión; el exhaustivo, para los releases.


Comparar configuraciones con sweeps

GuideLLM brilla para decidir entre configuraciones corriendo el mismo sweep contra cada una. El protocolo justo: misma carga (--data), mismo SLO, mismo hardware, variar solo la config. Ejemplos de decisión que un sweep resuelve con datos:

DecisiónQué compararQué revela el codo
FP16 vs FP8dos despliegues del mismo modeloFP8 suele dar codo más alto (más goodput)
max-num-seqsdistintos valores del flagel que maximiza goodput bajo SLO
vLLM vs SGLangdos motores, mismo modeloqué motor da más goodput a tu SLO
tamaño de KV/precisiónconfigs de KV cacheel efecto en la capacidad

Cada sweep da un codo; el codo más alto bajo el mismo SLO gana. Es el experimento controlado que llena la fila del cuadro de mando (artículo B8): no “X es más rápido” en abstracto, sino “X da Y tok/s de goodput más bajo este SLO concreto, lo que se traduce en Z réplicas menos y W € menos por millón de tokens”.


Observar los tres ejes durante el sweep

Un truco que conecta con el track de coste y energía: mientras corre el sweep, captura las métricas de GPU con DCGM. Así, de cada ronda sacas a la vez el goodput (GuideLLM), la potencia media (DCGM → J/token) y el coste (precio del nodo → €/token). En una sola campaña de sweep mides los tres ejes del cuadro de mando para cada punto de operación, y quedan coherentes por construcción (mismo instante, misma carga). En vez de un benchmark de rendimiento aislado, obtienes la fila completa —coste, rendimiento, energía— del codo, que es justo lo que la propuesta necesita. Alinea las ventanas temporales (la potencia de DCGM y las métricas de GuideLLM deben cubrir el mismo intervalo) o el J/token no corresponde al goodput medido.


Interpretar las distribuciones, no solo el codo

El codo es el titular, pero el informe HTML de GuideLLM da distribuciones completas, y ahí hay información que el codo resume. Tres lecturas adicionales:

  • La forma de la cola. Dos configs con el mismo P99 pueden tener colas distintas: una con P99 a 500 ms y P99,9 a 600 ms es estable; otra con P99 a 500 ms y P99,9 a 3.000 ms tiene una cola larga que afectará a algunos usuarios de forma severa. La media y hasta el P99 lo ocultan; la distribución lo enseña.
  • La separación TTFT/ITL. Ver las dos distribuciones por separado dice si el cuello es el prefill (TTFT alto) o el decode (ITL alto), lo que orienta la optimización (más concurrencia vs chunked prefill, etc.).
  • La dispersión entre rondas. Si el goodput varía mucho entre rondas a la misma tasa, el sistema es inestable bajo carga —algo que un único número de capacidad no revela—.

El codo dimensiona; las distribuciones diagnostican. Para una propuesta, el codo es el dato; para operar y optimizar, las distribuciones son donde se ve qué arreglar.


GuideLLM en el harness reproducible

GuideLLM encaja como el motor de carga del harness reproducible (artículo S4). El patrón:

  1. Un script/Job que despliega el motor con la config pineada, calienta, corre el sweep y vuelca el JSON con todos los metadatos (modelo, versión, hardware, --data, SLO).
  2. Nombrado por fecha y config para versionar las corridas.
  3. Almacén (repo git de JSONs o bucket) para reproducir y comparar.
  4. DCGM capturando en paralelo para añadir la energía a cada punto.

Así, “reproducir el codo de esta config” es un comando, no una tarde, y comparar dos releases es un diff de JSON. Es lo que convierte el benchmarking de una actividad puntual en una capacidad continua —y lo que permite que la cifra de capacidad de la propuesta venga con el banco para reproducirla.


El coste de un sweep (en euros)

Un sweep de 10 rondas a ~2 minutos por ronda ocupa el nodo ~20–30 minutos. A coste amortizado de ~11 €/h, son ~4–5,5 € por sweep completo de un modelo. No es mucho por corrida, pero un programa de benchmarking continuo (cada release, cada config, varios modelos) suma; por eso conviene automatizar el sweep como Job reproducible y correr sweeps cortos en CI (solo alrededor del codo) reservando el exhaustivo para los releases. El coste de medir es parte del coste de la plataforma —y pequeño frente al de dimensionar mal—.


El SLO por caso de uso: el codo se mueve con él

Como el SLO define el codo, distintos casos de uso dan capacidades distintas sobre el mismo hardware. Conviene tenerlo presente al dimensionar:

Caso de usoSLO típicoEfecto en el codo
Chat interactivoTTFT P99 < 500 ms, ITL < 50 mscodo temprano (latencia manda)
Copilot de códigoTTFT P99 < 300 mscodo aún más temprano
Batch / resúmenessin SLO de TTFT, maximizar throughputcodo tardío (casi throughput puro)
Agente (multi-paso)latencia por paso acotadadepende del nº de pasos

La misma GPU sirve más carga de batch que de chat interactivo, porque el SLO de batch es laxo. Por eso dimensionar requiere un sweep por perfil de carga: no hay “la capacidad del nodo”, hay “la capacidad del nodo para este SLO”. Mezclar cargas con SLOs distintos en el mismo pool sin separarlas es una fuente clásica de SLOs rotos —el batch satura y el chat interactivo lo paga—. La solución conecta con el scheduling: separar pools o prioridades por SLO.


GuideLLM vs los otros: cuándo usar cuál

Para situarlo frente a las herramientas de B2:

PreguntaHerramientaPor qué
“¿Hasta dónde cargo sin romper el SLO?”GuideLLMsweep dirigido por SLO, el codo
“¿Cuál es la capacidad máxima del endpoint?”AIPerfdetección automática de saturación
“¿Mejora mi cambio de flag en vLLM?”vllm bench serverápido, propio del motor
“¿Qué hardware/motor es mejor en abstracto?”leer MLPerfcomparabilidad cross-vendor

GuideLLM es la herramienta del dimensionamiento bajo SLO: cuando la pregunta es operativa (cuántas réplicas, qué coste por token a mi SLO), es la respuesta más directa. AIPerf y GuideLLM se solapan (ambos multi-proceso); la diferencia práctica es que GuideLLM está más orientado al SLO y al informe reproducible, y AIPerf a la capacidad máxima con detección automática. Muchos equipos usan los dos; lo que no se debe es comparar un resultado de uno con el de otro como si fueran la misma medida.


Checklist de un sweep defendible

Para que el codo de un sweep sostenga una decisión de sizing:

PasoVerificación
Carga realista--data con la distribución de tu tráfico (o poisson)
SLO declaradoTTFT/TPOT con percentil y umbral fijados
Sweep extendidollega hasta que el P99 rompe (pasa el codo)
Goodput, no throughputla capacidad es el goodput bajo SLO
Job en el clustersin latencia de red de cliente externo
Salida versionadaJSON con todos los metadatos
DCGM en paraleloenergía por punto, opcional pero recomendado

Si puedes marcar las siete casillas, el codo es un dato auditable; si falta cualquiera, es una anécdota. El sizing de la propuesta cuelga de que estas siete estén verdes.


Límites y trampas (data-driven)

  1. No extender el sweep más allá del codo. Si no detecta saturación, la capacidad estimada es la última ronda probada, no el codo real. Extiéndelo hasta que el P99 rompa.
  2. Carga irreal. Un --data de longitudes fijas no se parece a tu tráfico; usa la distribución real (o poisson) para un codo que aplique.
  3. Reportar throughput, no goodput. El codo se define por el goodput bajo SLO; el throughput bruto sobreestima la capacidad.
  4. SLO no declarado. Una capacidad sin el SLO bajo el que se midió no es comparable ni defendible.
  5. Cliente fuera del cluster. Correr GuideLLM desde fuera mete latencia de red en la medida; córrelo como Job dentro del cluster.

Con GuideLLM dominado, tienes la herramienta que convierte el rendimiento en un dato accionable: el codo, el sizing y el coste por token, todo de un sweep reproducible. El siguiente artículo (B4) entra en AIPerf de NVIDIA; este cierra la validación de SLO, que es la que dimensiona la propuesta.

Cierre

GuideLLM responde la única pregunta de rendimiento que dimensiona una plataforma: ¿hasta dónde cargo sin romper el SLO? Y la responde con la disciplina correcta —un sweep de idle a saturación que halla el codo, definido por el goodput bajo un SLO declarado, sobre una carga que se parece a la real—. De ese codo sale todo lo que la propuesta necesita: el número de réplicas, el coste por token (con el goodput como denominador, no el throughput de catálogo) y, si capturas DCGM durante el sweep, también la energía por token. El error que invalida el ejercicio es el de siempre: reportar el throughput máximo en vez del goodput, medir con una carga irreal, o no extender el sweep hasta ver dónde rompe. Hecho bien —como Job dentro del cluster, con la distribución real, el SLO fijado y la salida en JSON versionado— GuideLLM convierte el rendimiento de una cifra de marketing en un dato reproducible que aguanta una auditoría y dimensiona una arquitectura soberana con números. El codo no es el punto más rápido; es el punto donde tu plataforma sigue cumpliendo lo que prometió.

Ver también

Fuentes