Introdução: por que perfilamento de código Go é um must have
O desenvolvimento em Go (Golang) é conhecido por seu desempenho e eficiência. No entanto, até o código mais rápido pode ficar lento se não forem consideradas as particularidades do trabalho com memória, goroutines ou entrada/saída. Perfilamento é o processo de coleta de dados sobre a execução de um programa, que permite olhar "sob o capô" e entender quais funções consomem mais CPU, memória ou tempo.
Neste artigo, vamos explorar as ferramentas práticas de perfilamento em Go: pprof, trace e benchmarks integrados. Você aprenderá a encontrar gargalos e otimizar código com exemplos reais.
1. Principais ferramentas de perfilamento em Golang
O Go fornece um arsenal poderoso para perfilamento diretamente do pacote padrão. Vamos considerar três abordagens principais:
- pprof — perfilamento de CPU, memória, bloqueios e goroutines.
- trace — rastreamento de eventos: criação de goroutines, chamadas de sistema, GC.
- benchmark — teste de desempenho integrado usando
go test -bench.
Para começar com pprof, basta importar net/http/pprof no seu servidor HTTP ou usar runtime/pprof para aplicações CLI.
1.1. Como executar o perfilamento de CPU
A maneira mais simples é adicionar um endpoint ao seu servidor HTTP:
package main
import ( "log" "net/http" _ "net/http/pprof")
func main() { go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() // seu código principal select {}}Após a execução, abra http://localhost:6060/debug/pprof/ — você verá um painel. Para coletar um perfil de CPU por 30 segundos, execute:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30Isso baixará os dados e abrirá o console interativo do pprof.
2. Encontrando gargalos com pprof: CPU e memória
Suponha que temos uma função que processa um grande array de dados. Suspeitamos que ela está lenta. Vamos escrever um exemplo de teste e perfilá-lo.
2.1. Exemplo de código lento
package main
import ( "fmt" "time")
func slowFunction(data []int) int { sum := 0 for _, v := range data { time.Sleep(1 * time.Microsecond) // simulação de trabalho sum += v * v } return sum}
func main() { data := make([]int, 100000) for i := range data { data[i] = i } result := slowFunction(data) fmt.Println(result)}Vamos executar o perfilamento de CPU:
go run main.go &go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=10No navegador, será aberta uma visualização — o grafo de chamadas (call graph). A espessura das arestas mostra quanto tempo cada função consumiu. Veremos imediatamente que slowFunction e time.Sleep são os principais consumidores de CPU.
2.2. Análise de memória (heap profile)
Para encontrar vazamentos de memória ou objetos que ocupam muito espaço, use:
go tool pprof http://localhost:6060/debug/pprof/heapNo console interativo, você pode executar o comando top:
(pprof) top10Showing nodes accounting for 80MB, 100% of 80MB total flat flat% sum% cum cum% 40MB 50.00% 50.00% 40MB 50.00% main.slowFunction 20MB 25.00% 75.00% 20MB 25.00% main.createLargeSlice 20MB 25.00% 100.00% 20MB 25.00% runtime.makesliceIsso mostra quais funções estão alocando mais memória. Se você perceber que main.slowFunction está alocando 40MB, vale a pena considerar uma refatoração — por exemplo, usar reutilização de slices.