Go performance: pprof + escape analysis
- ⬜📐 Generics em Go (1.18+)(Go Profissional)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Ferramentas built-in são surpreendentemente boas
pprof, benchmark (testing.B), race detector, trace, escape analysis, flight-style tracing — tudo vem no binário do go. Você não precisa de APM pago para diagnosticar 90% dos gargalos. Basta saber ler o output.
Benchmark no testing.B
func BenchmarkParseJSON(b *testing.B) {
data := []byte(sample)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var u User
if err := json.Unmarshal(data, &u); err != nil {
b.Fatal(err)
}
}
}
// go test -bench=. -benchmem
// BenchmarkParseJSON-8 500000 3120 ns/op 512 B/op 7 allocs/opB/op e allocs/op são mais reveladores que ns/op. Alocação domina custo em serviços de alto throughput.
Escape analysis
// go build -gcflags="-m=2" ./...
func makeUser(name string) *User {
u := User{Name: name}
return &u // "moved to heap: u" (escapa via return)
}
func usesUser(name string) int {
u := User{Name: name}
return len(u.Name) // "u does not escape" → stack
}Interfaces fazem escape (valor precisa ir para heap para caber no par tipo/valor). Passar slice por valor copia header (barato), passar struct grande por valor copia tudo — use ponteiro em struct > 64 bytes.
pprof: CPU profile
// Em main.go
import _ "net/http/pprof"
go func() {
log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
}()# Coletar 30s de CPU profile em produção
go tool pprof -http=:9000 http://prod:6060/debug/pprof/profile?seconds=30
# Heap live
go tool pprof -http=:9000 http://prod:6060/debug/pprof/heap
# Goroutines (detectar leak)
curl http://prod:6060/debug/pprof/goroutine?debug=1 | lessO que a análise de escape responde, e por que isso importa para desempenho?
sync.Pool no caminho certo
var bufPool = sync.Pool{
New: func() any { return new(bytes.Buffer) },
}
func writeResponse(w io.Writer, v any) error {
buf := bufPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufPool.Put(buf)
}()
if err := json.NewEncoder(buf).Encode(v); err != nil {
return err
}
_, err := buf.WriteTo(w)
return err
}Pool esquece objetos entre GC runs — não conte com identidade. Reset obrigatório no Put, senão vaza estado entre requests.
sync/atomic para contadores lock-free
type Metrics struct {
Requests atomic.Int64
Errors atomic.Int64
}
m.Requests.Add(1)
r := m.Requests.Load()Fluxo real de otimização
1) Meça com benchmark ou pprof em staging. 2) Leia o flamegraph, encontre top CPU/alocação. 3) Corrija o hot path (reduzir escape, pool, atomic, evitar interface em loop). 4) Re-benchmark. Sem medir, você adivinha — e geralmente erra.
Perguntas frequentes
❓ Como investigar desempenho em Go?
❓ O que é análise de escape e por que importa?
❓ Reduzir alocação vale sempre a pena?
Fixando
Ao adotar um pool de buffers, qual é o cuidado obrigatório?
Qual é o fluxo de otimização recomendado?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…