Desempenho em Go: como perfilar e acelerar código

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Seu código Go está lento? Hora de olhar sob o capô

\n\n

Você escreveu um serviço em Go. Ele funciona. Mas quando a carga atinge 10.000 RPS, o servidor começa a "engasgar". Situação familiar? Go é famoso por seu desempenho "de fábrica", mas até mesmo nesta linguagem elegante existem armadilhas. Vazamentos de goroutines, gerenciamento ineficiente de memória, alocações desnecessárias — tudo isso mata a velocidade.

\n\n

Neste artigo, vamos explorar como o perfilamento Go ajuda a encontrar gargalos e mostrar técnicas específicas de otimização de aplicações Go. Você aprenderá como usar as ferramentas integradas pprof e go test -bench para transformar sua aplicação em um foguete. Vamos nessa!

\n\n

Kit de ferramentas do profiler: pprof e trace

\n\n

O Go fornece duas ferramentas poderosas para análise de desempenho: pprof (perfilamento de CPU, memória, bloqueios) e go tool trace (análise de concorrência e eventos GC).

\n\n

Incorporando pprof no servidor HTTP

\n\n

A maneira mais simples de começar o perfilamento é adicionar net/http/pprof ao seu servidor. Isso leva exatamente uma linha de importação:

\n\n
import (\n    _ "net/http/pprof"\n    "net/http"\n)\n\nfunc main() {\n    http.ListenAndServe(":8080", nil)\n}
\n\n

Agora, os perfis estão disponíveis em /debug/pprof/. Para capturar um perfil de CPU por 30 segundos, execute:

\n\n
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30
\n\n

Após a coleta dos dados, um console interativo será iniciado. O comando top15 mostrará as funções mais quentes:

\n\n
(pprof) top15\nShowing nodes accounting for 3.50s, 87.50% of 4.00s total\n      flat  flat%   sum%        cum   cum%\n     1.20s 30.00% 30.00%      1.50s 37.50%  main.heavyComputation
\n\n

Viu o main.heavyComputation? Este é nosso candidato para otimização. Para visualização, use web (se o Graphviz estiver instalado) — você obterá um grafo de chamadas.

\n\n

Perfilamento de memória: procurando vazamentos

\n\n

Vazamentos de memória em Go geralmente são vazamentos de goroutines ou referências esquecidas a objetos grandes. Vamos capturar um perfil heap:

\n\n
go tool pprof http://localhost:8080/debug/pprof/heap
\n\n

O comando alloc_objects mostrará quais funções criam mais objetos:

\n\n
(pprof) alloc_objects\n    2.50GB  100%  100%      2.50GB  100%  main.createLargeSlice
\n\n

Foi assim que encontramos uma função que aloca 2,5 GB de memória! Talvez ela esteja criando um slice enorme que pode ser reutilizado com sync.Pool.

\n\n

Escrevendo benchmarks: seu primeiro passo para otimização

\n\n

Antes de otimizar, é preciso medir. Os benchmarks Go são a ferramenta padrão para medir o desempenho de funções individuais.

\n\n

Exemplo: comparando concatenação de strings

\n\n

Suponha que estamos concatenando strings em um loop. Versão ruim:

\n\n
func ConcatSlow(parts []string) string {\n    result := ""\n    for _, p := range parts {\n        result += p\n    }\n    return result\n}
\n\n

Versão boa — usar strings.Builder:

\n\n
func ConcatFast(parts []string) string {\n    var b strings.Builder\n    for _, p := range parts {\n        b.WriteString(p)\n    }\n    return b.String()\n}
\n\n

Vamos escrever o benchmark (arquivo concat_test.go):

\n\n
func BenchmarkConcatSlow(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatSlow(parts)\n    }\n}\n\nfunc BenchmarkConcatFast(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatFast(parts)\n    }\n}
\n\n

Executamos:

\n\n
go test -bench=. -benchmem
\n\n

Resultado:

\n\n
BenchmarkConc                

Blogs

Book Recommendations