pstats en Python: analiza perfiles

Publicado el: 15/08/2026
Tempo de leitura: 5 minutos
Portátil con gráficos de rendimiento que representa análisis de perfiles con pstats en Python

El módulo pstats en Python lee, combina, ordena, filtra y presenta los datos producidos por cProfile y profile. El profiler registra llamadas, retornos y excepciones; pstats convierte esa información en reportes que ayudan a localizar árboles de llamadas lentos, invocaciones excesivas y algoritmos inadecuados.

Profiling no es benchmarking. El profiler determinista añade overhead y observa una ejecución completa. Usa timeit para comparar snippets pequeños en condiciones controladas y pstats para entender dónde gastó tiempo una ejecución representativa.

Genera un archivo de perfil

python -m cProfile -o perfil.prof aplicacion.py

Para perfilar un módulo:

python -m cProfile -o perfil.prof -m mi_paquete.cli

El dump no es un formato estable de intercambio. Analízalo con la misma versión, implementación y preferiblemente sistema operativo que lo creó.

Abre los datos con Stats

import pstats

estadisticas = pstats.Stats("perfil.prof")
estadisticas.print_stats()

El constructor también acepta un objeto cProfile.Profile, varios archivos o un stream personalizado.

Comprende las columnas

  • ncalls: cantidad total de llamadas.
  • tottime: tiempo dentro de la función, excluyendo subllamadas.
  • percall: tottime dividido por llamadas.
  • cumtime: tiempo en la función y todo lo llamado debajo.
  • percall acumulado: tiempo acumulado dividido por llamadas primitivas.

Las funciones recursivas pueden mostrar total/primitivas.

Ordena por tiempo acumulado

SortKey.CUMULATIVE muestra funciones cuyos árboles completos consumieron más tiempo.

from pstats import Stats, SortKey

Stats("perfil.prof") \
    .sort_stats(SortKey.CUMULATIVE) \
    .print_stats(20)

Suele ser la mejor primera vista para encontrar endpoints, tareas o algoritmos de alto nivel costosos.

Ordena por tiempo interno

SortKey.TIME clasifica el tiempo gastado directamente en el cuerpo, sin incluir callees.

Stats("perfil.prof") \
    .sort_stats(SortKey.TIME) \
    .print_stats(20)

Un cumtime alto con tottime bajo indica delegación. Si ambos son altos, la función hace trabajo importante directamente.

Usa SortKey

La enumeración es más robusta que abreviaturas. Incluye CALLS, PCALLS, FILENAME, LINE, NAME, NFL, STDNAME, TIME y CUMULATIVE.

estadisticas.sort_stats(
    SortKey.NAME,
    SortKey.FILENAME,
    SortKey.LINE,
)

Las claves adicionales resuelven empates. Evita la interfaz numérica antigua.

Elimina prefijos de directorio

strip_dirs() acorta el reporte:

estadisticas.strip_dirs() \
    .sort_stats(SortKey.TIME) \
    .print_stats()

El método modifica el objeto y puede fusionar entradas que se vuelven indistinguibles. Conserva otra instancia con rutas completas si son necesarias.

Restringe la salida

print_stats() acepta cantidad, fracción y expresiones regulares.

estadisticas.sort_stats(SortKey.CUMULATIVE)
estadisticas.print_stats(30)
estadisticas.print_stats(0.10)
estadisticas.print_stats("mi_paquete")

Las restricciones se aplican en orden. Reducir a la mitad y después filtrar es distinto de filtrar primero.

Inspecciona callers

print_callers() muestra qué funciones invocaron cada entrada.

estadisticas.print_callers("procesar_pedido")

Puede revelar un helper barato que se volvió costoso porque muchos caminos lo llaman repetidamente.

Inspecciona callees

print_callees() muestra la dirección opuesta.

estadisticas.print_callees("procesar_pedido")

Usa callers para localizar la demanda y callees para descomponer el trabajo interno.

Combina varias ejecuciones

El constructor puede combinar varios dumps. Las funciones con el mismo archivo, línea y nombre se acumulan.

estadisticas = pstats.Stats(
    "worker-1.prof",
    "worker-2.prof",
    "worker-3.prof",
)

Añade resultados posteriores con add():

estadisticas.add("worker-4.prof")

Combina solo ejecuciones comparables. Mezclar cargas, versiones, configuraciones o máquinas produce conclusiones engañosas.

Compara antes y después

pstats agrega archivos, pero no calcula automáticamente un delta estadístico entre versiones. Genera reportes equivalentes, exporta datos estructurados y compara funciones seleccionadas.

Repite escenarios, controla el tamaño de entrada y registra commit, entorno, versión y efectos de calentamiento.

Captura la salida

import io
import pstats
from pstats import SortKey

buffer = io.StringIO()
pstats.Stats("perfil.prof", stream=buffer) \
    .sort_stats(SortKey.CUMULATIVE) \
    .print_stats(20)

reporte = buffer.getvalue()

Sirve para artefactos de CI y dashboards. Elimina rutas absolutas y nombres sensibles antes de publicar.

Analiza un Profile en memoria

import cProfile
import pstats

profiler = cProfile.Profile()
profiler.enable()
ejecutar_escenario()
profiler.disable()

pstats.Stats(profiler) \
    .sort_stats(pstats.SortKey.CUMULATIVE) \
    .print_stats(15)

Esto evita archivos temporales. Guardar el dump sigue siendo útil para procesos largos y revisión posterior.

Exporta datos estructurados

get_stats_profile() devuelve una StatsProfile con registros FunctionProfile.

perfil = estadisticas.get_stats_profile()
for nombre, funcion in perfil.func_profiles.items():
    print(nombre, funcion.cumulative_time)

Facilita JSON y tablas. Los nombres pueden colisionar, por lo que conviene conservar archivo y línea.

Ejecutar pstats como módulo abre una interfaz de línea:

python -m pstats perfil.prof

Permite cargar, ordenar, filtrar y mostrar callers o callees. Utiliza conceptos de la guía de cmd en Python.

Tiempo acumulado y algoritmos

Una función con alto tiempo acumulado puede elegir un algoritmo caro, repetir I/O o invocar demasiado una dependencia. Microoptimizar sus propias líneas no siempre ayuda. Reducir trabajo, llamadas, asignaciones y viajes de red suele importar más.

Cantidad de llamadas

SortKey.CALLS destaca funciones muy invocadas.

estadisticas.sort_stats(SortKey.CALLS).print_stats(20)

Un helper mínimo puede dominar el tiempo si se llama millones de veces. Conteos inesperados también revelan callbacks duplicados o loops accidentales.

Tiempo y memoria por separado

pstats analiza llamadas y tiempo, no asignaciones. Para crecimiento de memoria, compara snapshots con tracemalloc en Python. Ejecuta ambas herramientas por separado cuando sea posible.

No confundas perfil con bytecode

Los reportes se organizan por funciones, archivos y líneas. Para instrucciones compiladas, consulta opcode en Python y dis en Python.

Rendimiento de imports

Si el perfil muestra imports costosos, modulefinder en Python ayuda a mapear dependencias estáticas. Los imports dinámicos y efectos superiores requieren medición real.

Limitaciones

  • El profiler añade overhead.
  • Funciones C pueden parecer demasiado rápidas.
  • Llamadas muy breves acumulan error.
  • Una ejecución puede no representar producción.
  • El I/O externo varía con el entorno.
  • Threads y procesos requieren recolección planificada.

Usa el perfil para formular hipótesis y confirma mejoras con mediciones repetidas.

Protege los dumps

Los archivos pueden revelar rutas, módulos, arquitectura, funciones de negocio y patrones de uso. Almacénalos con control de acceso y no cargues dumps desconocidos en entornos sensibles.

El formato no garantiza compatibilidad futura y no debe convertirse en API pública.

Buenas prácticas

  • Empieza por CUMULATIVE y luego usa TIME.
  • Filtra al código de la aplicación.
  • Revisa callers y callees.
  • Repite cargas representativas.
  • Registra entorno y commit.
  • No trates profiling como benchmark exacto.
  • Protege archivos de perfil.
  • Mide nuevamente después de optimizar.

Conclusión

pstats en Python transforma datos del profiler en reportes accionables. Orden, filtros, agregación, callers, callees y perfiles estructurados ayudan a distinguir funciones directamente lentas de costos causados por delegación o frecuencia.

Consulta la documentación oficial de pstats y la documentación de timeit.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026