Módulo 8 · Rendimiento y caché

Lección 37 — Perfilado

Medir antes de optimizar: dónde se va realmente el tiempo.

Publicada
En esta lección
  1. Ejercicio 1 — La línea base
  2. Ejercicio 2 — El N+1 con disfraz
  3. Ejercicio 3 — El micro-perfil honesto
  4. Ejercicio 4 — py-spy en el worker
  5. Ejercicio 5 — El informe de perfilado
  6. Entrega

Medir antes de optimizar, sobre TicketFlow. Sin solutions.md hasta entregar.

Ejercicio 1 — La línea base

  1. Escribe el test de perfil local: 500 reservas de un mismo evento con cProfile y top 12 por cumulative. Pega el output completo.
  2. La hipótesis: lee el output y escribe UNA hipótesis del tipo "el X% del tiempo está en Y por Z". ¿Es la función que sospechabas o otra?
  3. La mejora mínima que tu hipótesis sugiere (sin tocar BD) y la re-medición con el MISMO test: pega el antes/después del tiempo total y del top 3.

Ejercicio 2 — El N+1 con disfraz

  1. Implementa la vista de "mis reservas" con el N+1 DELIBERADO: serializer con SerializerMethodField que consulta la tarifa por fila. Mide con django-debug-toolbar (o silk): ¿cuántas queries para 40 reservas?
  2. Arréglalo con select_related/prefetch_related y re-mide. ¿Cuántas queries y cuánto tiempo?
  3. El segundo disfraz: el mismo serializer accediendo a reserva.seats.all() dentro del loop (lazy evaluation del related manager). ¿Por qué prefetch_related lo cura y qué pasa si SOLO haces .iterator()? (pista: el N+1 es por fila, no por lote).

Ejercicio 3 — El micro-perfil honesto

  1. Con timeit: compara str(uuid) vs f"{uuid}" vs uuid.hex sobre 100k iteraciones. ¿Importa? Anota el número real (esperado: no importa; la lección es Aprender a decir no).
  2. line_profiler sobre calcular_total() (la 08): ¿qué línea concentra el tiempo? ¿Es el Decimal o el query a la tarifa? (si es la query: es el N+1 de nuevo, no un problema de CPU).
  3. El hallazgo del snapshot: aplica la denormalización del §5 (precalcula total_amount al confirmar) y verifica con el test de la 08 que el snapshot de precio se mantiene (el precio de la tarifa puede cambiar después; el total de la reserva NO).

Ejercicio 4 — py-spy en el worker

  1. Lanza el stack local (gunicorn con 4 workers + la suite k6 del 36 en modo humo) y haz py-spy dump sobre un worker DURANTE la meseta: ¿en qué está? Pega el dump de los 3 workers.
  2. El flamegraph: py-spy record de 60 s durante la meseta y pega el SVG (o el top de pila si lo exportas a texto). ¿Qué función domina? ¿Coincide con el cuello de BD del 36 o descubrió algo nuevo?
  3. El caso colgado: simula un worker colgado (una tarea Celery que duerme 120 s) y usa py-spy dump para diagnosticar SIN reiniciar: ¿qué le dirías al on-call con solo el dump?

Ejercicio 5 — El informe de perfilado

  1. Escribe el informe docs/perf/2027-09-checkout.md con: línea base (k6), hallazgos por herramienta (cProfile/silk/py-spy), mejoras aplicadas y re-medición, y las NO-mejoras decididas (¿qué viste lento y NO vale la pena arreglar?).
  2. La regla del proyecto: cada mejora de rendimiento en un PR lleva SU before/after medido. Escribe la plantilla del PR (5 líneas) y pégala en CONTRIBUTING.md.

Entrega

Pega el output de cProfile, el antes/después del N+1, el dump de py-spy y el informe. Después: Lección 38 — Estrategias de caché.