---
title: "GuideLLM a fondo: validar el SLO bajo carga y dimensionar desde el codo"
url: https://blog.lo0.es/posts/guidellm-validacion-slo-bajo-carga/
markdown: https://blog.lo0.es/posts/guidellm-validacion-slo-bajo-carga/index.md
lang: es
date: 2026-06-14
lastmod: 2026-06-17
author: "José Manuel Román"
description: "Deep dive de datos sobre GuideLLM: los rate-types (synchronous, concurrent, poisson, throughput, sweep), el sweep automático que halla el codo, cómo definir el SLO, la salida (HTML/JSON/CSV), cómo dimensionar réplicas y coste por token desde el codo, el despliegue como Job de Kubernetes y la validación en CI. Con comandos y fuentes."
tags: ["benchmarking","guidellm","vllm","slo","goodput","sizing","capacity-planning","serie-datos"]
translations:
  en: https://blog.lo0.es/en/posts/guidellm-validating-slo-under-load-sizing-knee/index.md
---

# 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](https://developers.redhat.com/articles/2025/06/20/guidellm-evaluate-llm-deployments-real-world-inference)).
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-type` | Qué hace | Cuándo |
|---|---|---|
| **synchronous** | una petición cada vez | latencia base, sin concurrencia |
| **concurrent** | mantiene N peticiones simultáneas fijas | medir a una concurrencia concreta |
| **poisson** | peticiones por segundo según Poisson | simular tráfico real (llegadas aleatorias) |
| **throughput** | satura el motor al máximo | capacidad bruta máxima |
| **sweep** | barrido automático de idle a máximo | **hallar el codo** (el más útil) |

En modo `concurrent`, el `--rate "1,2,4"` lanza tres benchmarks a esas concurrencias
([Medium · GuideLLM en OpenShift](https://medium.com/@jajodia.nirjhar/exploring-guidellm-benchmarking-a-live-llm-on-openshift-ccc2d0841794)).
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](https://developers.redhat.com/articles/2025/12/24/how-deploy-and-benchmark-vllm-guidellm-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**.

> **Diagrama:** El sweep de GuideLLM: 10 rondas de idle a maximo, el throughput satura y la latencia se dispara, marcando el codo como capacidad segura bajo SLO — [verlo en la versión HTML](https://blog.lo0.es/posts/guidellm-validacion-slo-bajo-carga/)

---

## 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:

| Ronda | Tasa (req/s) | TTFT P99 (ms) | ITL P50 (ms) | Throughput (tok/s) | Goodput (tok/s) |
|---|---|---|---|---|---|
| 1 | 2 | 95 | 19 | 480 | 480 |
| 3 | 8 | 140 | 20 | 1.900 | 1.900 |
| 5 | 14 | 240 | 22 | 3.100 | 3.060 |
| 6 | 17 | 460 | 24 | 3.400 | 3.330 |
| 7 | 20 | 760 | 31 | 3.700 | 2.520 |
| 9 | 26 | 1.500 | 48 | 3.950 | 900 |
| 10 | 30 | 2.400 | 71 | 4.000 | 380 |

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étrica | Umbral de ejemplo | Qué protege |
|---|---|---|
| **TTFT P99** | < 500 ms | la espera al primer token (interactividad) |
| **TPOT / ITL P95** | < 50 ms/token | la "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:

```bash
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 | `--data` | Fidelidad |
|---|---|---|
| Longitud fija | `prompt_tokens=1024,output_tokens=256` | baja: el tráfico real no es fijo |
| Distribución sintética | rangos de longitud | media |
| Trazas reales | dataset de tu tráfico | alta: 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](https://developers.redhat.com/articles/2025/12/24/how-deploy-and-benchmark-vllm-guidellm-kubernetes)).
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**:

> **Diagrama:** Del codo de GuideLLM al sizing: el goodput por replica, la carga objetivo, el numero de replicas y el coste por token — [verlo en la versión HTML](https://blog.lo0.es/posts/guidellm-validacion-slo-bajo-carga/)

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](https://blog.lo0.es/posts/capacity-planning-inferencia-llm-on-premise/index.md) 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](https://developers.redhat.com/articles/2025/12/24/how-deploy-and-benchmark-vllm-guidellm-kubernetes)).
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ón | Qué comparar | Qué revela el codo |
|---|---|---|
| FP16 vs FP8 | dos despliegues del mismo modelo | FP8 suele dar codo más alto (más goodput) |
| `max-num-seqs` | distintos valores del flag | el que maximiza goodput bajo SLO |
| vLLM vs SGLang | dos motores, mismo modelo | qué motor da más goodput a tu SLO |
| tamaño de KV/precisión | configs de KV cache | el 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 uso | SLO típico | Efecto en el codo |
|---|---|---|
| Chat interactivo | TTFT P99 < 500 ms, ITL < 50 ms | codo temprano (latencia manda) |
| Copilot de código | TTFT P99 < 300 ms | codo aún más temprano |
| Batch / resúmenes | sin SLO de TTFT, maximizar throughput | codo tardío (casi throughput puro) |
| Agente (multi-paso) | latencia por paso acotada | depende 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:

| Pregunta | Herramienta | Por qué |
|---|---|---|
| "¿Hasta dónde cargo sin romper el SLO?" | **GuideLLM** | sweep dirigido por SLO, el codo |
| "¿Cuál es la capacidad máxima del endpoint?" | AIPerf | detección automática de saturación |
| "¿Mejora mi cambio de flag en vLLM?" | vllm bench serve | rápido, propio del motor |
| "¿Qué hardware/motor es mejor en abstracto?" | leer MLPerf | comparabilidad 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:

| Paso | Verificación |
|---|---|
| Carga realista | `--data` con la distribución de tu tráfico (o poisson) |
| SLO declarado | TTFT/TPOT con percentil y umbral fijados |
| Sweep extendido | llega hasta que el P99 rompe (pasa el codo) |
| Goodput, no throughput | la capacidad es el goodput bajo SLO |
| Job en el cluster | sin latencia de red de cliente externo |
| Salida versionada | JSON con todos los metadatos |
| DCGM en paralelo | energí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

- [GenAI-Perf a fondo](https://blog.lo0.es/posts/nvidia-genai-perf-a-fondo/index.md) — el perfilador de NVIDIA y cómo se compara con GuideLLM ficha a ficha (rate-types, sweep de concurrencia, métricas).
- [Comparativa de motores de serving (vLLM/SGLang/TRT-LLM/Dynamo)](https://blog.lo0.es/posts/comparativa-motores-serving-pareto/index.md) — tras validar los SLOs con GuideLLM, esta comparativa decide qué motor cumple la frontera de Pareto goodput-latencia.
- [Sesgo de medición y reproducibilidad](https://blog.lo0.es/posts/sesgo-medicion-reproducibilidad-bench/index.md) — los errores de setup que hacen que un sweep de concurrencia resulte no reproducible, aunque la herramienta esté bien configurada.

## Fuentes

- Red Hat · GuideLLM: evaluar despliegues LLM para inferencia real — https://developers.redhat.com/articles/2025/06/20/guidellm-evaluate-llm-deployments-real-world-inference
- Red Hat · desplegar y benchmarkear vLLM con GuideLLM en Kubernetes — https://developers.redhat.com/articles/2025/12/24/how-deploy-and-benchmark-vllm-guidellm-kubernetes
- GuideLLM · PyPI — https://pypi.org/project/guidellm/
- GuideLLM · GitHub (proyecto vLLM) — https://github.com/vllm-project/guidellm
- Medium · GuideLLM en OpenShift (rate-types, ejemplo) — https://medium.com/@jajodia.nirjhar/exploring-guidellm-benchmarking-a-live-llm-on-openshift-ccc2d0841794

