Módulo 14 - Performance e profiling
Achar o gargalo com cProfile e pstats
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Rodar o cProfile sobre uma função ou programa inteiro.
- Ler as colunas do relatório: ncalls, tottime e cumtime.
- Ordenar o resultado com pstats para achar o gargalo.
- Distinguir tempo próprio da função de tempo acumulado.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Achar o gargalo com cProfile e pstats.
Os objetivos desta aula. Rodar o cProfile sobre uma função ou programa inteiro. Ler as colunas do relatório: ncalls, tottime e cumtime. Ordenar o resultado com pstats para achar o gargalo. Distinguir tempo próprio da função de tempo acumulado.
Veja o essencial, parte por parte.
Perfilando o programa inteiro. cProfile acompanha cada chamada de função do programa.
Ordenando o relatório com pstats. Comece ordenando por tottime: o topo é onde o trabalho bruto está.
Do perfil à decisão de otimizar. O perfil não otimiza nada sozinho; ele aponta onde vale investir.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Perfilando o programa inteiro
Enquanto o timeit responde qual trecho é mais rápido, o cProfile responde onde meu programa gasta tempo. Ele é um profiler determinístico: acompanha cada entrada e saída de função durante a execução real e monta uma tabela com quantas vezes cada função foi chamada e quanto tempo consumiu. É a foto exata de onde o tempo foi parar, sem palpite. A forma mais simples de usar é chamar cProfile.run com a expressão que você quer perfilar.
import cProfile
def eh_primo(n):
if n < 2:
return False
for d in range(2, int(n ** 0.5) + 1):
if n % d == 0:
return False
return True
def contar_primos(limite):
return sum(1 for n in range(limite) if eh_primo(n))
cProfile.run("contar_primos(100000)")cProfile.run perfila a expressão e imprime uma tabela com todas as funções chamadas.
O relatório impresso tem uma linha por função, com colunas que valem entender. A coluna ncalls diz quantas vezes a função foi chamada. A coluna tottime mostra o tempo gasto apenas no corpo daquela função, sem contar o que ela chama por dentro. A coluna cumtime mostra o tempo cumulativo, incluindo tudo que a função aciona. No exemplo, contar_primos tem cumtime alto, porque engloba todas as chamadas a eh_primo, mas tottime baixo, porque o trabalho pesado mesmo acontece dentro de eh_primo.
Ordenando o relatório com pstats
Em programas reais, a tabela do profiler tem dezenas ou centenas de linhas, e ler tudo é inviável. O módulo pstats resolve isso: ele carrega o resultado do profiler e deixa você ordenar e filtrar. O passo prático é salvar o perfil em um arquivo e depois abrir com pstats, pedindo a ordenação por tempo. Ordenar por tottime coloca no topo as funções onde o trabalho de fato acontece; ordenar por cumtime destaca as funções que, direta ou indiretamente, dominam a execução.
import cProfile
import pstats
cProfile.run("contar_primos(100000)", "perfil.out")
p = pstats.Stats("perfil.out")
p.sort_stats("tottime") # ordena pelo tempo proprio de cada funcao
p.print_stats(5) # mostra so as 5 funcoes mais custosaspstats carrega o perfil salvo, ordena por tempo próprio e mostra só o topo da lista.
| Coluna | Significa | Quando olhar |
|---|---|---|
| ncalls | Número de chamadas da função | Suspeita de excesso de chamadas |
| tottime | Tempo só no corpo da função | Achar onde o trabalho pesado roda |
| cumtime | Tempo da função mais o que ela chama | Achar a origem de uma cadeia lenta |
| percall | Tempo médio por chamada | Comparar custo unitário entre funções |
As colunas do relatório do profiler e para que serve cada uma.
Do perfil à decisão de otimizar
O perfil não otimiza nada sozinho; ele aponta onde vale investir. A regra prática é a lei do desequilíbrio: quase sempre uma fração pequena do código responde pela maior parte do tempo. Encontrar essa fração é o objetivo. Depois de identificar a função campeã, você decide a estratégia: talvez ela seja chamada vezes demais e a solução seja evitar chamadas, com memoização; talvez ela use uma estrutura de dados ruim para a busca que faz; talvez o algoritmo em si tenha uma complexidade alta. As próximas aulas dão essas ferramentas.
Um detalhe honesto: o cProfile adiciona uma sobrecarga, porque acompanha cada chamada. Os tempos absolutos ficam maiores do que na execução normal, então não os use como medida final de velocidade. O que importa no perfil é a proporção entre as funções, ou seja, quem consome mais em relação às outras. Para o número final de uma otimização específica, volte ao timeit ou meça o programa real sem o profiler ativo. O perfil aponta o alvo; a medição limpa confirma o ganho.
Teste rápido
No relatório do cProfile, qual a diferença entre tottime e cumtime?
Perguntas frequentes
- Quando usar cProfile em vez de timeit?
- Use o cProfile quando quiser descobrir onde um programa inteiro gasta tempo, função por função. Use o timeit quando já souber qual trecho quer afinar e precisar comparar duas versões dele com precisão. Na prática, o cProfile aponta o gargalo e o timeit mede a alternativa.
- Por que os tempos do cProfile parecem maiores que o normal?
- Porque o profiler acompanha cada chamada de função, e isso adiciona sobrecarga. Os tempos absolutos ficam inflados. O que importa no perfil é a proporção entre as funções, não o número final. Para medir o ganho real de uma otimização, use uma medição limpa, sem o profiler ativo.
- Como salvo o perfil para analisar depois?
- Passe um nome de arquivo como segundo argumento para cProfile.run, por exemplo cProfile.run('funcao()', 'perfil.out'). Depois abra esse arquivo com pstats.Stats('perfil.out') e use sort_stats e print_stats para ordenar e ver só as funções mais custosas.
- Ordeno por tottime ou por cumtime?
- Comece por tottime, que coloca no topo as funções onde o trabalho bruto acontece. Use cumtime para subir a cadeia de chamadas e descobrir qual função de alto nível está, no fundo, disparando todo esse trabalho. Os dois olhares se complementam.
- O cProfile serve para código com concorrência ou async?
- Ele funciona, mas a leitura fica mais delicada, porque tempo esperando entrada e saída aparece misturado com tempo de CPU. Para código assíncrono, foque no tempo de CPU das funções e lembre que a espera por rede ou disco não é gargalo de processamento. Ferramentas específicas de perfil ajudam nesses casos.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.