pstats no Python: analise perfis

Publicado em: 15/08/2026
Tempo de leitura: 6 minutos
Notebook com gráficos de desempenho representando análise de perfis com pstats no Python

O módulo pstats no Python lê, combina, ordena, filtra e apresenta os dados produzidos por cProfile e profile. O profiler coleta eventos de chamadas, retornos e exceções; o pstats transforma esse volume de dados em relatórios que ajudam a localizar funções lentas, chamadas excessivas e algoritmos inadequados.

Profiling não é benchmarking. O profiler adiciona overhead e mede a aplicação inteira sob instrumentação. Use timeit para comparar pequenos trechos em condições controladas e pstats para entender onde uma execução real gastou tempo.

Gere um arquivo de perfil

A linha de comando do cProfile pode gravar os dados em um arquivo binário:

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

Quando o alvo é um módulo:

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

O arquivo não é um formato de intercâmbio estável. Analise-o com a mesma versão, implementação e, idealmente, sistema operacional que o criou.

Abra os dados com Stats

import pstats

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

O construtor também aceita um objeto cProfile.Profile, vários arquivos ou um stream customizado para a saída.

Entenda as colunas

O relatório padrão apresenta:

  • ncalls: total de chamadas.
  • tottime: tempo dentro da função, sem subfunções.
  • percall: tottime dividido pelas chamadas.
  • cumtime: tempo na função e em tudo que ela chamou.
  • percall cumulativo: tempo acumulado dividido por chamadas primitivas.

Em funções recursivas, ncalls pode aparecer como total/primitivas.

Ordene por tempo cumulativo

SortKey.CUMULATIVE mostra funções cujas árvores de chamadas consumiram mais tempo.

from pstats import Stats, SortKey

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

Essa é uma boa primeira visão para identificar endpoints, tarefas ou algoritmos de alto nível responsáveis pelo custo.

Ordene por tempo interno

SortKey.TIME destaca funções que gastam tempo no próprio corpo, excluindo subchamadas.

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

Uma função com cumtime alto e tottime baixo provavelmente delega o custo. Uma função com ambos altos executa trabalho significativo diretamente.

Use SortKey

A enumeração é mais robusta que abreviações em texto. Chaves úteis incluem CALLS, PCALLS, FILENAME, LINE, NAME, NFL, STDNAME, TIME e CUMULATIVE.

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

Chaves adicionais servem como critérios de desempate. O comportamento numérico legado deve ser evitado em código novo.

Remova prefixos de caminhos

strip_dirs() deixa o relatório mais curto.

stats.strip_dirs().sort_stats(SortKey.TIME).print_stats()

O método modifica o objeto e pode unir entradas que passam a ter mesmo arquivo, linha e nome. Guarde uma versão sem strip quando caminhos completos forem necessários.

Filtre a saída

print_stats() aceita quantidade, fração e expressões regulares.

stats.sort_stats(SortKey.CUMULATIVE)
stats.print_stats(30)
stats.print_stats(0.10)
stats.print_stats("meu_pacote")

Restrições são aplicadas na ordem em que aparecem. print_stats(.5, 'servico') primeiro reduz a metade e depois filtra; inverter os argumentos produz outro resultado.

Veja quem chamou uma função

print_callers() responde quais funções chamaram cada entrada selecionada.

stats.sort_stats(SortKey.CUMULATIVE)
stats.print_callers("processar_pedido")

Esse relatório ajuda a encontrar um helper barato que ficou caro por ser invocado por muitos caminhos.

Veja o que uma função chamou

print_callees() mostra a direção oposta.

stats.print_callees("processar_pedido")

Use callers para entender origem da demanda e callees para decompor o custo interno.

Combine várias execuções

O construtor aceita múltiplos arquivos e agrega funções identificadas pela mesma combinação de arquivo, linha e nome.

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

add() incorpora dados posteriores:

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

Combine apenas execuções comparáveis. Misturar versões, configurações, volumes ou sistemas diferentes pode produzir conclusões enganosas.

Compare antes e depois

pstats soma arquivos, mas não calcula automaticamente um delta estatístico entre duas versões. Gere relatórios equivalentes, exporte dados estruturados e compare funções relevantes.

Repita várias execuções com a mesma carga, descarte aquecimento quando aplicável e registre commit, versão do Python, máquina e configuração.

Capture a saída em uma string

import io
import pstats
from pstats import SortKey

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

relatorio = buffer.getvalue()

Isso permite anexar o relatório a artefatos de CI, dashboards ou tickets. Remova caminhos e dados sensíveis antes de publicar.

Use um Profile em memória

import cProfile
import pstats

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

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

Essa abordagem evita arquivo temporário e é útil em testes de desempenho internos. Para processos longos, gravar o dump preserva evidências para análise posterior.

Exporte dados estruturados

get_stats_profile() retorna uma StatsProfile com objetos FunctionProfile.

perfil = stats.get_stats_profile()
for nome, funcao in perfil.func_profiles.items():
    print(nome, funcao.cumulative_time)

A API facilita JSON, tabelas e alertas, mas nomes de função podem colidir. Preserve arquivo e linha quando construir identificadores.

O navegador interativo

Executado como módulo, pstats abre uma interface de linha de comando:

python -m pstats perfil.prof

O navegador permite carregar, ordenar, filtrar e mostrar callers ou callees. Ele usa uma interface baseada no módulo cmd no Python.

Tempo cumulativo e algoritmos

Um alto cumtime pode indicar que a função escolheu um algoritmo caro ou chamou repetidamente uma operação externa. Otimizar linhas internas não resolve sempre o problema; reduzir quantidade de trabalho, chamadas e alocações costuma trazer maior impacto.

Quantidade de chamadas

SortKey.CALLS destaca funções invocadas muitas vezes.

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

Um helper minúsculo pode dominar o custo quando aparece milhões de vezes. Também pode revelar loops involuntários e callbacks duplicados.

Profiling e memória

pstats mede chamadas e tempo, não alocações. Para crescimento de memória, compare snapshots com tracemalloc no Python. Use as duas ferramentas separadamente para evitar overhead excessivo e resultados difíceis de interpretar.

Não confunda com bytecode

O relatório é organizado por funções, arquivos e linhas, não por instruções. Para estudar operações compiladas, consulte opcode no Python e dis no Python.

Imports e tempo de inicialização

Quando o perfil mostra custo elevado em imports, modulefinder no Python ajuda a mapear dependências estáticas. Mesmo assim, imports dinâmicos e efeitos de módulos precisam de medição real.

Limitações do profiling determinístico

  • O profiler adiciona overhead.
  • Funções C podem parecer desproporcionalmente rápidas.
  • Operações muito curtas acumulam erro de medição.
  • Uma única execução pode não representar a produção.
  • I/O externo varia com ambiente.
  • Threads e processos exigem coleta planejada.

Use os dados para formular hipóteses e valide alterações com medições repetidas.

Segurança dos arquivos de perfil

Trate dumps como artefatos internos. Eles podem revelar caminhos, nomes de módulos, arquitetura, funções de negócio e padrões de uso. Armazene com controle de acesso e não aceite dumps de origem desconhecida sem isolamento.

O formato não possui garantia de compatibilidade futura e não deve ser usado como API pública.

Boas práticas

  • Comece por CUMULATIVE e depois use TIME.
  • Filtre para o código da aplicação.
  • Inspecione callers e callees.
  • Repita cenários representativos.
  • Registre ambiente e commit.
  • Não trate profiling como benchmark preciso.
  • Proteja os dumps.
  • Confirme melhorias com nova medição.

Conclusão

O pstats no Python transforma dados brutos de profiling em relatórios acionáveis. Ordenação, filtros, agregação, callers, callees e perfis estruturados ajudam a distinguir funções diretamente lentas de operações caras por delegação ou frequência.

Consulte a documentação oficial do pstats e a documentação do timeit.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Notebook com código representando exemplos executáveis testados com doctest no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    doctest no Python: teste exemplos

    Aprenda doctest no Python para executar exemplos em docstrings e arquivos, normalizar saídas e integrar documentação ao CI.

    Ler mais

    Tempo de leitura: 6 minutos
    15/08/2026
    Código em tela representando navegação de classes e funções com pyclbr no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pyclbr no Python: inspecione módulos

    Aprenda pyclbr no Python para listar classes, funções, métodos e definições aninhadas sem importar nem executar o módulo.

    Ler mais

    Tempo de leitura: 6 minutos
    15/08/2026
    Monitor com código binário representando instruções opcode do bytecode do Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    opcode no Python: explore o bytecode

    Aprenda opcode no Python para mapear instruções de bytecode, argumentos, saltos, caches e efeitos de pilha usando dis.

    Ler mais

    Tempo de leitura: 6 minutos
    15/08/2026
    Desenvolvedor analisando consumo de memória com tracemalloc no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc no Python: encontre vazamentos

    Aprenda tracemalloc no Python para comparar snapshots, encontrar crescimento de memória e diagnosticar vazamentos com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    15/08/2026
    Código com anotações de tipos representando introspecção com annotationlib no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib no Python: leia anotações

    Aprenda annotationlib no Python 3.14 para ler anotações como valores, ForwardRef ou strings e evitar riscos de execução.

    Ler mais

    Tempo de leitura: 7 minutos
    14/08/2026
    Arquivadores organizados representando módulos importados diretamente de arquivos ZIP com zipimport no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    zipimport no Python: importe de ZIPs

    Aprenda zipimport no Python para importar módulos e pacotes de arquivos ZIP, trabalhar com loaders e evitar riscos de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    14/08/2026