Skip to content

English · Español

Break 00 — Servir sin batching, ver el throughput colapsar

🇪🇸 La forma más rápida de sentir el valor del batching es quitarlo. Este /break desactiva el BatchingScheduler y deja que cada petición use una matmul propia. Bajo C=1 todo va bien; bajo C=8 la cola crece sin freno y la p95 se dispara.


Qué vas a hacer

Desactivar la capa de batching en el servidor de inferencia para que cada petición ejecute su propio forward pass, end-to-end serializado en el hilo BLAS de NumPy. Conducir el servidor con el generador de carga del lab 02 y observar el throughput colapsar.

Paso 1 — Localiza el scheduler

src/miniserve/scheduler.py        # the BatchingScheduler class (Phase 33 lab 02)
src/miniserve/handlers.py         # the /correct handler that submits to it

Paso 2 — Introduce el bug (≤ 5 líneas)

En src/miniserve/handlers.py, cambia la ruta de envío para que el handler salte el scheduler y llame a model.forward() directamente:

# OLD
result = await scheduler.submit(request_id, prompt_ids)

# NEW (the broken version)
result = model.forward(prompt_ids)   # synchronous, no batch coalescing

Es el diff más pequeño posible. No cambian imports. El servidor sigue arrancando. El endpoint sigue respondiendo.

Paso 3 — Registra el break

learners/borja/phase-33/notes/breaks.md:

- bug-id: 33-01
  concept: continuous batching
  symptom: p50 doubles at C=2, quadruples at C=4, server queue OOMs at C=16.
  hidden_cause: handlers.py bypasses scheduler.submit(); each request serializes
                on the NumPy BLAS thread, no batch coalescing happens.
  hint_1: "Plot p50 vs concurrency. What shape do you see? Linear? Sublinear?"
  hint_2: "Add a log line in BatchingScheduler.submit(). Does it print under load?"
  hint_3: "grep for 'model.forward' in src/miniserve/. Should that call appear there?"
  fix_diff: revert handlers.py — submit to scheduler instead of calling forward directly.

Paso 4 — Verifica que es observable

Ejecuta loadgen.py del lab 02 con --concurrency 8 --duration 60s. Output esperado:

p50:  1180 ms   (target: ≤ 250 ms)
p95:  3400 ms   (target: ≤ 600 ms)
rps:   6.2      (target: ≥ 30 rps)
errors: 12% timeout

Los tests del lab 02 en tests/phase33/test_throughput.py se pondrán en rojo.

Paso 5 — El momento de enseñanza

La métrica a mirar fijamente es p50 vs concurrencia. Con batching, la curva es aproximadamente logarítmica — la matmul de BLAS amortiza el trabajo entre peticiones. Sin batching, es lineal — cada petición se serializa en la CPU. Borja debería poder derivar cuál de las dos está en el código a partir de la forma de la curva antes de leer handlers.py.

Cuando esté atascado, la cascada de pistas camina desde "mira los datos" → "mira los logs del scheduler" → "mira el call site". El fix es un revert de una línea; la lección es que "scheduler" en tu diagrama de arquitectura no hace nada salvo que los handlers enruten a través de él.

Reglas duras respetadas

  • Un solo bug.
  • Reversible en 1 línea.
  • Observable desde el test de carga (assertion fallando + gráfico visiblemente malo).
  • Sin implicaciones de seguridad.
  • Los tests no se modifican.

Siguiente: lee ../theory/02-static-vs-continuous-batching.md una vez que el test esté verde de nuevo.