Módulo 6: Modal — Deployment serverless de LLMs
Cost optimization
Tu endpoint funciona. Ya sabes hacer autoscaling. Ahora viene la pregunta que te van a hacer cuando muestres esto en una reunión: "¿Cuánto nos cuesta cada request? ¿Y si servimos 1M al mes?"
Esta cápsula te enseña a contestar eso con números y a aplicar las optimizaciones que más impacto tienen. No es teoría — al final vas a tener una hoja de cálculo mental de cuánto te cuesta cada deployment y vas a poder explicarle a tu cofounder dónde puedes recortar 50% sin sacrificar producto.
Al terminar vas a poder:
- Calcular el costo real por request de tu deployment (GPU + CPU + storage + egress)
- Elegir la GPU correcta según tu modelo y throughput esperado
- Aplicar las tres optimizaciones de mayor impacto: batching, cuantización, sizing de GPU
- Decidir cuándo Modal deja de ser barato y deberías cambiar a alternativa
El modelo mental: tres componentes de costo
Tu factura de Modal se descompone en:
| Componente | Cuándo se cobra | Orden de magnitud |
|---|---|---|
| GPU time | Por segundo, mientras un container con GPU está prendido | $$$$ (lo grueso) |
| CPU time | Por segundo, para containers sin GPU | $ |
| Storage | Por GB-mes, por todos los volumes y imágenes | $ |
| Egress | Por GB transferido fuera de Modal | $ (negligible para chat texto) |
95% del costo de un deployment LLM = GPU time. Por eso esta cápsula se enfoca casi exclusivamente en optimizar GPU.
Calcular cost-per-request: el método
Tres números te dan el costo:
costo_por_request ≈ tiempo_gpu_por_request × costo_por_segundo_de_GPU
Paso 1 — Mide cuánto GPU usa tu request
Logs de Modal te dicen cuánto tarda cada request en GPU. Para un request promedio de tu Mistral:
modal app logs mistral-api --tail 200 | grep "handled request"
# [container-abc123] handled request in 1.82s
# [container-def456] handled request in 2.04s
# [container-ghi789] handled request in 1.91s
Promedio aproximado: ~2s de GPU por request.
Paso 2 — Mira el precio por segundo de tu GPU
De modal.com/pricing — a inicios de 2026, precios aproximados (verifica el actual):
| GPU | Costo por segundo aprox |
|---|---|
| T4 | ~$0.00017 |
| A10G | ~$0.00030 |
| L4 | ~$0.00025 |
| A100 40GB | ~$0.00100 |
| H100 | ~$0.00250 |
Con A10G y 2s por request:
costo_por_request = 2 × $0.00030 = $0.0006
Paso 3 — Escala a tu volumen
| Volumen | Costo GPU mensual |
|---|---|
| 1,000 requests | $0.60 |
| 100,000 requests | $60 |
| 1,000,000 requests | $600 |
| 10,000,000 requests | $6,000 |
Más warm pool: si min_containers=1 mantiene una A10G prendida 24/7, suma ~$720/mes adicional sea cual sea el volumen.
Comparación con alternativas a este punto
Es útil tener referencia. Para 1M requests/mes (~2 req/s sostenidos, 100 tokens output promedio):
| Opción | Costo aprox/mes | Notas |
|---|---|---|
| Modal (Mistral 7B + warm pool) | ~$600 + $720 = $1,320 | Tu modelo, tu pricing |
| OpenAI GPT-4o-mini | ~$300-500 | Token-based; depende de longitud |
| Self-hosted Ollama (A10G en AWS 24/7) | ~$1,800 | GPU prendida siempre |
| OpenRouter (variable) | ~$200-1,500 | Según modelo elegido |
Modal con warm pool no siempre gana. Si tu volumen es alto y constante, OpenAI puede ser más barato. Si tienes restricciones de privacidad o de modelo, Modal sigue siendo justificado aunque cueste un poco más.
Las cuatro optimizaciones de mayor impacto
Optimización 1 — Batching (ganancia: 2-4×)
vLLM batchea automáticamente requests concurrentes en una sola pasada de GPU. Tu cliente puede enviar múltiples prompts juntos:
@modal.method()
def generar_batch(self, prompts: list[str], max_tokens: int = 256) -> list[str]:
from vllm import SamplingParams
sampling = SamplingParams(temperature=0.7, max_tokens=max_tokens)
prompts_fmt = [f"[INST] {p} [/INST]" for p in prompts]
outputs = self.llm.generate(prompts_fmt, sampling)
return [o.outputs[0].text.strip() for o in outputs]
Medición: procesar 8 prompts uno por uno = 8 × 2s = 16s. Procesar los 8 en batch = ~5s. Costo por request baja ~3×.
Aplicabilidad: funciona si tu uso lo permite (procesos batch, embebido en pipelines async). No funciona para un chatbot 1-a-1 donde cada usuario espera su respuesta.
Optimización 2 — Cuantización (ganancia: 2-3× en throughput, ~10% pérdida de calidad)
Mistral 7B en fp16 ocupa 14GB y procesa ~50 tokens/s en A10G. La versión cuantizada AWQ (4-bit) ocupa ~4GB y procesa ~150 tokens/s. Esto significa:
- Tu request se completa 2-3× más rápido → menos tiempo de GPU → menos costo
- Cabe en GPUs más chicas (T4 sirve para AWQ) → costo por segundo más bajo
MODELO = "TheBloke/Mistral-7B-Instruct-v0.2-AWQ" # versión cuantizada
@app.cls(
image=imagen.pip_install("autoawq"),
gpu="T4", # ¡cabe en T4 ahora!
...
)
class MistralServiceAWQ:
@modal.enter()
def cargar(self):
from vllm import LLM
self.llm = LLM(model=MODELO, quantization="awq")
Trade-off: la cuantización pierde un poco de precisión. Para tareas conversacionales generales, casi imperceptible. Para tareas que requieren razonamiento preciso (matemáticas, código), evalúa antes de adoptar.
Optimización 3 — GPU sizing correcto (ganancia: 30-60% por cambio de GPU)
Más caro no siempre = mejor para tu caso. La pregunta importante: ¿estás "GPU-bound" o "memory-bound"?
| Si estás... | Síntoma | Acción |
|---|---|---|
| Memory-bound | "Out of memory" o KV cache lleno | Pasa a GPU con más VRAM (A10G→L4 no ayuda; A10G→A100 sí) |
| Compute-bound | GPU al 100% utilization, latencia alta | Pasa a GPU más rápida (A10G→L4 o A100) |
| Sobredimensionado | GPU al 30% utilization, latencia ok | Baja a GPU más chica |
Ver utilización: modal app logs o el dashboard de Modal muestran métricas de GPU. Si tu A10G nunca pasa de 40% utilización, una L4 o T4 (con cuantización) sirve igual y cuesta menos.
Optimización 4 — Reducir max_tokens razonablemente (ganancia: lineal)
Tu latencia (y costo) es proporcional al número de tokens generados. Generar 256 tokens cuesta 2× que generar 128. Si tu producto no necesita respuestas larguísimas:
class ChatRequest(BaseModel):
prompt: str
max_tokens: int = Field(150, ge=1, le=512) # default 150, no 512
Combina con prompt engineering: pide al modelo que sea conciso ("Responde en 3 frases"). Funciona sorprendentemente bien.
Storage: el componente olvidado
Los Volume cobran ~$0.10/GB/mes. Si tienes:
mistral-pesos(14GB) → ~$1.40/mesllama-pesos(40GB) → ~$4/mes- 10 versiones de modelos viejos olvidadas → puede sumar
modal volume list # ver tus volumes
modal volume du mistral-pesos # tamaño exacto
modal volume delete xxx # eliminar
No es un montón, pero vale la pena auditarlo trimestralmente. Cuesta cero borrar un volume viejo.
Cuándo Modal deja de ser barato
Tres señales de que Modal ya no es el camino correcto:
Señal 1 — Tráfico muy constante y muy alto (>10 req/s 24/7). Si tu GPU está saturada todo el tiempo, "pago por uso" termina siendo más caro que "renta fija". Un A10G en AWS reservado cuesta ~$0.40/hr (vs $1+/hr en Modal). Si tu utilización es 95%, self-hosted gana.
Señal 2 — Necesitas GPUs muy específicas que Modal no ofrece. H200, MI300 (AMD), TPUs — Modal no las tiene. Si tu modelo requiere alguna, pasa a Replicate, RunPod, o cloud directo.
Señal 3 — Compliance estricto que Modal no certifica. HIPAA, SOC2 specific, on-prem mandatorio — verifica certificaciones actuales. Modal SOC2 Type II existe, pero HIPAA puede ser conversación con el equipo de ventas.
Para el 80% de los casos en startups, Modal es óptimo. Las señales de arriba son excepciones.
Trampas comunes
Trampa 1 — "El warm pool me cuesta más que el ahorro." Calcula honesto: si tu min_containers=1 cuesta $720/mes y solo evitas 100 cold starts al mes, no vale la pena (cada cold start cuesta ~$0.05). Solo vale el warm pool si la latencia mejorada genera valor real (usuarios reales que abandonarían si esperan 30s).
Trampa 2 — "Tengo cuenta gratis, ¿por qué me cobran?" El free tier de Modal es $30/mes en créditos. Si te excedes, te cobran la diferencia. Verifica tu uso en el dashboard antes de fin de mes. Pon billing alerts.
Trampa 3 — "Cuantización pareció gratis pero mi calidad bajó mucho." Mide antes de adoptar. Ten un set de 20-50 prompts representativos. Genera respuestas con fp16 y con AWQ. Compara manualmente o con un LLM-as-judge. Si la diferencia de calidad te importa para tu caso, no adoptes.
Trampa 4 — "Estoy pagando GPU mientras el modelo se descarga."
Sí. La descarga inicial de 14GB es ~2-4 min de GPU pagada. Optimízalo: pre-bakea pesos en la imagen (vimos esto en la cápsula 06) o usa hf_transfer para acelerar 3-5×.
Trampa 5 — "Mi tráfico es burst pero max_containers=20 me da picos de costo."
Tres opciones: (a) baja max_containers y aguanta queueing en bursts; (b) implementa rate limiting frente; (c) agrega caché de respuestas frecuentes (LangCache, semantic cache) para que requests repetidos no toquen GPU.
Ejercicio: estima tu costo mensual
Tu producto tiene este perfil:
- 50,000 requests/día
- Promedio 150 tokens output
- Mistral 7B en A10G
- 90% del tráfico en horario laboral (9am-6pm)
- Requieres warm pool durante horario laboral
Estima:
- Costo de GPU activa (mientras genera respuestas)
- Costo de warm pool durante horario laboral
- Costo total mensual aproximado
- Si decides cuantizar a AWQ y bajar a T4, ¿cuál sería el nuevo total aprox?
Ver solución
Asumiendo: A10G a $0.00030/s, T4 a $0.00017/s, ~2s/request en A10G fp16, ~0.7s/request en T4 AWQ.
1. Costo de GPU activa (A10G fp16):
- 50,000 req/día × 30 días = 1,500,000 req/mes
- 1,500,000 × 2s × $0.00030 = $900/mes
2. Costo de warm pool (horario laboral):
- 9hrs/día × 22 días laborales/mes ≈ 200hrs/mes
- 200hrs × 3600s × $0.00030 = $216/mes
3. Total aprox: $900 + $216 + storage(~$2) = ~$1,120/mes
4. Con AWQ en T4:
- 1,500,000 × 0.7s × $0.00017 = $179/mes (GPU activa)
- 200hrs × 3600s × $0.00017 = $122/mes (warm pool T4)
- Total: $179 + $122 + $2 = ~$303/mes
Ahorro: ~73%. Vale la pena medir si la calidad se mantiene aceptable para tu caso de uso.
Resumen
Aprendiste:
- ✅ Descomponer tu factura: GPU (el grueso) + CPU + storage + egress
- ✅ Calcular cost-per-request con dos números: tiempo GPU × precio por segundo
- ✅ Cuatro optimizaciones grandes: batching, cuantización, GPU sizing, max_tokens
- ✅ Cuándo Modal deja de ser óptimo: tráfico constante alto, GPUs específicas, compliance estricto
- ✅ Hacer un estimado defensible para una reunión con cofounder/PM
Checkpoint: si te preguntan "¿cuánto cuesta tu deployment al mes con X tráfico?" y puedes dar un número con cálculo, no solo "barato" o "no sé", estás listo.
Siguiente cápsula
En 08 — Proyecto: API escalable vas a integrar todo lo del módulo en un endpoint production-ready: con autoscaling configurado, cost optimization aplicado, monitoring básico, y testing. Es el deliverable que vas a usar en el proyecto final del Módulo 8 (Unified Client) y, ojalá, en algo real tuyo.
Recursos
- Modal Pricing — precios actuales por GPU.
- vLLM Quantization Guide — AWQ paso a paso.
- Modal — Billing dashboard — tu uso actual.
- TheBloke on Hugging Face — colección de modelos cuantizados listos para vLLM.
- Semantic caching for LLMs — reducir requests duplicados que tocan GPU.