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

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026