AI Development

vLLM vs SGLang en 2026: Serví LLMs Más Rápido

12 de septiembre de 2026
11 min de lectura
Racks de servidores GPU con luces de estado en un data center oscuro
Compartir:

Tu chatbot se arrastra bajo carga, el dashboard de GPU muestra 60% de uso y ya agrandaste la instancia dos veces. El modelo está bien — el cuello de botella es el motor de inferencia, y seguís corriendo el setup por defecto copiado de un tutorial de hace dos años.

En este deep-dive comparo vLLM y SGLang con números reales de 2026 en H100, explico RadixAttention vs prefix caching en cinco minutos y te doy comandos verificados, un cliente compatible con OpenAI, structured outputs y un mini-benchmark para decidir con tu workload — no con un titular.

1. El Problema: El Benchmark Decía 29%, Tu p99 No Se Movió

Cada pocas semanas un post nuevo dice que un motor supera al otro por 29% — los equipos migran, debuggean conflictos de kernels a la medianoche y ven que la latencia p99 quedó exactamente igual. Ambos motores se pasan uno al otro en cada release, así que un delta de leaderboard es ruido. Lo que realmente decide al ganador es la forma de tus prompts: prompts únicos no se comportan nada como loops de RAG donde cada request comparte un system prompt de 4k.

El Error Más Común

Migrar de motor por un número de titular. El famoso 29% de ventaja de SGLang se midió en Llama 3.1 8B con tráfico de prefijos compartidos — en Llama 3.3 70B el mismo lab midió un gap de 3–5%, y en una RTX 4090 con prompts únicos un tercer lab midió un empate. Benchmarkeá tus prompts, no los de otro.

El objetivo de esta guía: entender las dos filosofías de caché, servir el mismo modelo en ambos motores con comandos verificados, pegarle a ambos con código idéntico y salir con una regla de decisión que puedas defender en una design review.

2. Conceptos Mínimos que Realmente Necesitás

Cuatro ideas explican casi todos los benchmarks que vas a leer este año. Con esto, los números de abajo se leen solos.

🧱

PagedAttention (origen vLLM)

Paginado de KV cache · SOSP 2023

vLLM partió el KV cache en bloques paginados como la memoria de un SO, eliminando fragmentación y habilitando continuous batching. Su prefix caching automático es por hash: bloques completos de 16 tokens con hashes idénticos se reutilizan entre requests — casi gratis, sin cambiar outputs.

🌳

RadixAttention (origen SGLang)

Reuso con radix tree · NeurIPS 2024

SGLang conserva el KV cache de requests terminados en un radix tree y reutiliza prefijos compartidos a nivel de token — few-shots, historial de chat, templates de agentes, contexto RAG. Cuanto más largo tu prefijo compartido, mayor el ahorro de prefill y menor tu time-to-first-token.

⏱️

TTFT vs ITL vs Throughput

Lo que siente el usuario · lo que factura el GPU

TTFT (tiempo al primer token) es lo que siente el usuario de chat; ITL (latencia entre tokens) es la fluidez del streaming; throughput (tokens/seg) es lo que pagás por hora de GPU. El prefix caching ataca el costo de prefill: mueve TTFT y throughput en workloads con prefijos — rara vez el ITL de cola.

✂️

Prefill/Decode Desagregado

Experimental · ambos motores

Separa prefill y decode en instancias distintas para tunear TTFT e ITL por separado y controlar la latencia de cola. La doc oficial de vLLM es explícita: el prefill desagregado NO mejora el throughput — es una herramienta de latencia, no un speedup.

Lo que Realmente Dicen los Números 2026 en H100

El roundup de Techsy de septiembre 2026 en H100 (corridas de Spheron con Llama 3.3 70B FP8 + corridas de PremAI con Llama 3.1 8B) más el empate de ComputingForGeeks en RTX 4090 cuentan una historia consistente: SGLang se escapa con prefijos compartidos y modelos chicos; el gap colapsa en modelos grandes y prompts únicos.

Llama 3.3 70B FP8 · H100

2.400 vs 2.460 tok/s @ conc 100 · TTFT p50 740 vs 710 ms

≈3% de gap — elegí cualquiera

Llama 3.1 8B · H100 · prefijos compartidos

12.500 vs 16.200 tok/s (SGLang +29%)

SGLang gana con prefijos

Qwen2.5-7B · RTX 4090 · mixto

5.221 vs 5.155 tok/s — dentro del ruido

Empate honesto

El Modelo Mental en un Párrafo

vLLM es el default seguro: mayor cobertura de modelos, pipeline parallelism para modelos muy grandes, la comunidad más grande. SGLang gana workloads con prefijos compartidos, generación estructurada y MoE por ~10–30% end to end. Ambos exponen endpoints compatibles con OpenAI, así tu cliente casi no cambia — lo que difieren son los flags del servidor.

3. Tutorial: Un Modelo en Ambos Motores, Paso a Paso

Vamos a servir Llama 3.1 8B Instruct en vLLM (:8000) y SGLang (:30000) en el mismo host con GPU, probar ambos, llamarlos con Python idéntico, forzar JSON en ambos y correr una comparación de carga de 5 minutos. Seis pasos, solo comandos oficiales.

Paso 1

Serví con vLLM (+ prefix caching activado)

El CLI serve de vLLM envuelve al servidor compatible con OpenAI. --enable-prefix-caching activa la caché automática de prefijos por hash (apagada por defecto en algunos builds); --gpu-memory-utilization 0.9 reserva VRAM para pesos más KV cache igual que la fracción de memoria de SGLang.

pip install vllm openai

vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --enable-prefix-caching \
  --gpu-memory-utilization 0.9

Paso 2

Serví con SGLang (mismo modelo, puerto 30000)

El launcher de SGLang descarga los pesos en la primera corrida, aloca el pool de KV e imprime el sizing cuando está listo. --mem-fraction-static 0.9 es la porción de VRAM para pesos más KV cache — el análogo directo del flag de vLLM de arriba.

pip install sglang openai

python3 -m sglang.launch_server \
  --model-path meta-llama/Llama-3.1-8B-Instruct \
  --host 0.0.0.0 --port 30000 \
  --mem-fraction-static 0.9

Paso 3

Smoke-test de ambos endpoints

Ambos servidores exponen /v1/models y /v1/chat/completions. Si estos dos curls no listan tu modelo, arreglá el servidor antes de tocar código cliente — nueve de diez veces es un puerto mal o un OOM cargando pesos.

curl http://localhost:8000/v1/models
curl http://localhost:30000/v1/models

Paso 4

Un cliente Python, ambos motores

Este es el patrón verificado compatible con OpenAI de la doc de SGLang — solo cambia base_url. Notá el system prompt compartido: esa es justo la forma de prefijo donde RadixAttention rinde, así que mantené los prompts idénticos o la comparación no vale nada.

from openai import OpenAI

SYSTEM = 'You are a concise release-notes writer for developer tools.'
USER = 'Summarize these changes in 3 bullets: faster KV cache, new tokenizer, fixed streaming.'

def ask(base_url):
    client = OpenAI(base_url=base_url, api_key='EMPTY')
    r = client.chat.completions.create(
        model='meta-llama/Llama-3.1-8B-Instruct',
        messages=[{'role': 'system', 'content': SYSTEM},
                  {'role': 'user', 'content': USER}],
        temperature=0, max_tokens=128,
    )
    return r.choices[0].message.content

print('vLLM:  ', ask('http://localhost:8000/v1'))
print('SGLang:', ask('http://localhost:30000/v1'))

Paso 5

Structured outputs en ambos

Los agentes necesitan JSON que parsee. vLLM acepta guided_json en extra_body (decoding con gramática); SGLang acepta response_format json_object igual que OpenAI. Mismo schema, ambos motores, cero hacks de parsing.

schema = {'type': 'object', 'properties': {
    'title': {'type': 'string'}, 'bullets': {'type': 'array', 'items': {'type': 'string'}}},
    'required': ['title', 'bullets']}

# vLLM: JSON con gramática
vllm = OpenAI(base_url='http://localhost:8000/v1', api_key='EMPTY')
r1 = vllm.chat.completions.create(model='meta-llama/Llama-3.1-8B-Instruct',
    messages=[{'role': 'user', 'content': USER}],
    extra_body={'guided_json': schema}, max_tokens=256)

# SGLang: modo JSON estilo OpenAI
sgl = OpenAI(base_url='http://localhost:30000/v1', api_key='EMPTY')
r2 = sgl.chat.completions.create(model='meta-llama/Llama-3.1-8B-Instruct',
    messages=[{'role': 'user', 'content': USER}],
    response_format={'type': 'json_object'}, max_tokens=256)

Paso 6

Mini-benchmark: barrido de concurrencia en 5 minutos

Mismos prompts, misma escalera de concurrencia, ambos puertos. Medí tokens totales sobre tiempo de pared — ese es el throughput que mapea a tu factura de GPU. Esperá que SGLang abra ventaja a medida que crece la porción de prefijo compartido; con prompts únicos esperá un empate.

import asyncio, time
from openai import AsyncOpenAI

PROMPTS = [USER] * 32  # reemplazá con TUS prompts de producción

async def bench(base_url, conc=16):
    client = AsyncOpenAI(base_url=base_url, api_key='EMPTY')
    sem = asyncio.Semaphore(conc)
    async def one(p):
        async with sem:
            r = await client.chat.completions.create(
                model='meta-llama/Llama-3.1-8B-Instruct',
                messages=[{'role': 'system', 'content': SYSTEM},
                          {'role': 'user', 'content': p}],
                max_tokens=128)
            return len(r.choices[0].message.content.split())
    t0 = time.time()
    toks = sum(await asyncio.gather(*[one(p) for p in PROMPTS]))
    dt = time.time() - t0
    return toks / dt

for conc in (1, 8, 32):
    v = await bench('http://localhost:8000/v1', conc)
    s = await bench('http://localhost:30000/v1', conc)
    print(f'conc={conc:3d}  vLLM={v:7.1f} tok/s  SGLang={s:7.1f} tok/s')

Cómo Sabés que Funcionó

Ambos curls a /v1/models listan Llama 3.1 8B, el script del paso 4 imprime dos resúmenes sanos, ambas respuestas del paso 5 pasan json.loads al primer intento, y tu barrido del paso 6 muestra el gap creciendo con la porción de prefijo compartido — esa curva, no un número suelto, es tu business case de migración.

4. Errores Comunes que Invalidan tu Comparación

Ya revisé suficientes shootouts de motores para oler los failure modes de lejos. Evitá estos cinco y tus números van a sobrevivir al contacto con producción.

🙈

Benchmarkear con prompts únicos y citar el gap de prefix caching

Los prompts de evals y data-gen casi no comparten nada, así que RadixAttention no tiene nada para reutilizar. Si tu tráfico es prompts únicos, esperá el resultado de la RTX 4090 — un empate — y elegí por ecosistema, no por throughput. El benchmark tiene que parecerse a TU tráfico.

🚩

Olvidar --enable-prefix-caching en vLLM

La caché automática de prefijos de vLLM es un flag del servidor, no un campo por request — y solo cachea bloques completos. Comparar vLLM stock contra el radix tree siempre activo de SGLang es amañar el test. Seteá el flag, y recordá que los bloques parciales de cola nunca hacen hit.

💥

OOM al cargar: fracciones de VRAM copiadas sin pensar

Copiar --gpu-memory-utilization 0.9 o --mem-fraction-static 0.9 a ciegas en una placa más chica (o con otro tenant al lado) muere durante la captura de CUDA graphs. Dimensioná desde el log del pool que SGLang imprime al arrancar, dejá margen para contextos largos y re-tuneá por GPU — los números de 4090 no se transfieren a H100.

🔀

Desplegar serving desagregado para “ir más rápido”

El split prefill/decode es experimental y, según la doc oficial, NO mejora el throughput — cambia complejidad operativa por control de TTFT/ITL y colas más calmas. Si tu problema es tokens-por-dólar, arreglá batching y caching primero; desagregá solo con SLOs interactivos estrictos.

🔌

Asumir que los clientes son 100% intercambiables

Las llamadas base de chat portan limpio, pero los flags avanzados divergen: guided_json / guided_regex / parsers de tool-calls de vLLM vs response_format / reasoning parsers / sintaxis LoRA model:adapter de SGLang. Auditá cada campo de extra_body antes de cambiar de servidor o tus agentes se rompen de formas nuevas y excitantes.

Regla de oro

Empezá con vLLM por cobertura, cambiá a SGLang cuando el profiling pruebe que el throughput con prefijos compartidos o generación estructurada es la restricción. Re-coré TU barrido cada trimestre — ambos proyectos sacan releases cada pocas semanas y el 29% de hoy es el empate del trimestre que viene.

Conclusión

En H100s de 2026 el resumen honesto entra en una línea: SGLang gana el serving con prefijos compartidos en modelos chicos por hasta ~29%, empata en modelos grandes y prompts únicos, y ambos hablan APIs compatibles con OpenAI así que migrar cuesta un cambio de config más una auditoría de flags.

Corré los seis pasos de arriba con tus prompts de producción esta semana. Si la curva de concurrencia se dobla hacia SGLang, migrá los servicios con prefijos y dejá el resto en vLLM — las flotas heterogéneas son normales y ambos motores conviven detrás del mismo load balancer.

Framework de Decisión: Resumen

Elegí SGLang cuando

  • • RAG / chat / agentes comparten prefijos largos
  • • Pipelines pesados de structured outputs
  • • Modelos MoE (clase DeepSeek)

Elegí vLLM cuando

  • • Los prompts son mayormente únicos (evals, batch)
  • • Necesitás pipeline parallelism / arqs exóticas
  • • Importan más las integraciones y la doc

Siempre

  • • Activá prefix caching explícitamente
  • • Benchmarkeá tus prompts cada trimestre
  • • Auditá extra_body antes de cambiar

Fuentes

Diego Rodriguez

Diego Rodriguez

Ingeniero Senior Full-Stack & AI

Diego tiene mas de 9 anos de experiencia construyendo aplicaciones potenciadas por IA de produccion, desde orquestacion de LLMs y pipelines RAG hasta deteccion de riesgos con ML y sistemas de trading algoritmico.

Conoce mas sobre Diego