Perfilamento de código Go: encontrando gargalos para iniciantes
\n\nQuando seu projeto Go começa a ficar lento, consumir muita memória ou funcionar de forma instável, surge uma pergunta natural: "Onde está o gargalo?" Palpites intuitivos raramente ajudam — são necessários dados precisos. Perfilamento (profiling) é o processo de coletar métricas sobre o funcionamento do programa: quanto tempo cada função leva, quanta memória é alocada, com que frequência o coletor de lixo é chamado. Neste artigo, vamos explorar como usar as ferramentas integradas do Go (pprof e trace) para encontrar e corrigir rapidamente problemas de desempenho.
\n\nO que é perfilamento e por que ele é necessário?
\n\nPerfilamento é como um raio-X para o seu código. Você não vê apenas "o programa está lento", mas números concretos: a função parseData() ocupa 40% do tempo, enquanto loadConfig() aloca 2 GB de memória. Para Go, existem vários tipos de perfis:
- \n
- Perfil de CPU — mostra quais funções consomem tempo de processador. \n
- Perfil de memória (heap) — mostra quais funções alocam memória e onde os objetos residem. \n
- Perfil de Goroutine — mostra o número de goroutines ativas e seu estado. \n
- Perfil de Bloqueio — mostra onde as goroutines estão bloqueadas em mutexes ou canais. \n
- Perfil de Mutex — mostra a contenção por mutexes. \n
Para iniciantes, os mais úteis são os perfis de CPU e Memória. Eles fornecem 80% das respostas para a pergunta "por que o código está lento".
\n\nFerramenta pprof: o perfilador integrado do Go
\n\nGo vem com o pacote integrado net/http/pprof. Ele adiciona endpoints ao seu servidor HTTP, dos quais você pode coletar perfis. Vamos ver um exemplo básico.
Passo 1: Conectando pprof à aplicação
\n\npackage main\n\nimport (\n "fmt"\n "log"\n "net/http"\n _ "net/http/pprof" // conecta o perfilador\n "time"\n)\n\nfunc main() {\n // Inicia o servidor na porta 8080\n go func() {\n log.Println(http.ListenAndServe("localhost:8080", nil))\n }()\n\n // Nossa lógica de negócio "pesada"\n for i := 0; i < 100; i++ {\n go heavyTask(i)\n }\n\n // Aguarda a conclusão\n time.Sleep(10 * time.Second)\n fmt.Println("Done")\n}\n\nfunc heavyTask(id int) {\n // Simulação de trabalho pesado\n for i := 0; i < 1000000; i++ {\n _ = i * i\n }\n fmt.Printf("Task %d finished\\n", id)\n}\n\n\nApós iniciar a aplicação, abra o navegador em http://localhost:8080/debug/pprof/. Você verá uma interface web com links para os perfis.
Passo 2: Coletando o perfil de CPU
\n\nA maneira mais simples é usar a linha de comando. Execute:
\n\ngo tool pprof http://localhost:8080/debug/pprof/profile?seconds=30\n\nEste comando coletará o perfil de CPU por 30 segundos. Após a conclusão, o console interativo do pprof será aberto. Digite top para ver as funções mais "quentes":
(pprof) top\nShowing nodes accounting for 1.50s, 100% of 1.50s total\n flat flat% sum% cum cum%\n 1.50s 100% 100% 1.50s 100% main.heavyTask\n 0 0% 100% 1.50s 100% runtime.main\n\n\nAqui, flat é o tempo gasto diretamente dentro da função, e cum é o tempo incluindo chamadas de funções filhas. No nosso exemplo, 100% do tempo é gasto em heavyTask.
Passo 3: Visualização via interface web
\n\nPara uma análise mais visual, use o modo gráfico:
\n\ngo tool pprof -http=:8081 http://localhost:8080/debug/pprof/profile?seconds=30\n\nUm navegador será aberto com um gráfico de chamadas interativo. Quanto maior e mais vermelho o bloco, mais recursos ele consome. Esta é uma ótima maneira de encontrar rapidamente funções "pesadas".
\n\n