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

    Portátil con código que representa ejemplos ejecutables probados con doctest en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    doctest en Python: prueba ejemplos

    Aprende doctest en Python para ejecutar ejemplos en docstrings y archivos, normalizar salidas e integrar documentación con CI.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Código en pantalla que representa navegación de clases y funciones con pyclbr en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pyclbr en Python: inspecciona módulos

    Aprende pyclbr en Python para listar clases, funciones, métodos y definiciones anidadas sin importar ni ejecutar el módulo.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Monitor con código binario que representa instrucciones opcode del bytecode de Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    opcode en Python: explora el bytecode

    Aprende opcode en Python para mapear instrucciones de bytecode, argumentos, saltos, caches y efectos de pila mediante dis.

    Ler mais

    Tempo de leitura: 5 minutos
    15/08/2026
    Desarrollador investigando consumo de memoria con tracemalloc en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc en Python: detecta fugas

    Usa tracemalloc en Python para comparar snapshots, localizar crecimiento de memoria e investigar fugas en aplicaciones.

    Ler mais

    Tempo de leitura: 6 minutos
    15/08/2026
    Código con anotaciones de tipos que representa introspección con annotationlib en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib en Python: lee anotaciones

    Aprende annotationlib en Python 3.14 para recuperar anotaciones como valores, ForwardRef o strings y controlar riesgos de ejecución.

    Ler mais

    Tempo de leitura: 5 minutos
    14/08/2026
    Archivadores organizados que representan módulos importados directamente desde archivos ZIP con zipimport en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    zipimport en Python: importa desde ZIP

    Aprende zipimport en Python para cargar módulos y paquetes desde archivos ZIP, usar importadores y proteger sistemas de plugins.

    Ler mais

    Tempo de leitura: 5 minutos
    14/08/2026