Performance em Go: Meça, Perfis e Otimize
Go é rápido o suficiente para a maioria dos serviços sem otimizações heroicas.
Busque em todas as páginas da documentação
Go é rápido o suficiente para a maioria dos serviços sem otimizações heroicas.
Quando latência ou custo importam, a cultura da linguagem é explícita: prove o gargalo com dados, corrija o caminho crítico comprovado e mantenha o código legível em todos os outros lugares.
Fundamentos de Performance coleta snippets de medição executáveis; artigos irmãos cobrem pprof, tracing, análise de escape, padrões de alocação, PGO e ajuste de GC.
testing.B), pprof (CPU/heap/mutex/block), execution tracer (go tool trace), escape analysis, alocações, GOGC/GOMEMLIMIT, PGO.unsafe e pools adicionam custo de manutenção que deve superar os ganhos medidos.Programas Go gastam tempo em três grandes categorias: seu código, o runtime (scheduler, GC, reflection) e o SO (syscalls, rede, disco).
A otimização começa identificando qual categoria domina para sua carga de trabalho.
Benchmarks (go test -bench) respondem a micro-perguntas: a implementação A é mais rápida que a B em um tamanho de entrada fixo?
Eles rodam em um processo controlado com caches de CPU aquecidos e são ideais para serialização, parsing e loops apertados.
Perfis (runtime/pprof, net/http/pprof) respondem a macro-perguntas: quais funções consomem CPU ou heap sob concorrência semelhante à de produção?
Perfis de CPU são amostras estatísticas da pilha de chamadas.
Perfis de heap mostram de onde se originam as alocações e quanta memória viva cada site de chamada retém.
Traces (runtime/trace) capturam uma linha do tempo: agendamento de goroutines, eventos STW do GC, bloqueio de syscall, e esperas de rede.
Use traces quando pprof mostra CPU baixa, mas a latência ainda é alta.
O compilador inlines funções pequenas, elimina verificações de limites quando pode provar segurança, e aplica análise de escape para decidir alocação em stack vs heap.
Você o influencia com código idiomático mais frequentemente do que com diretivas //go:noinline.
O garbage collector é geracional e concorrente.
A taxa de alocação impulsiona o comportamento de CPU e pausas do GC.
Reduzir ponteiros e reutilizar buffers frequentemente supera ajustar os knobs do GOGC.
Um loop de investigação típico se parece com isto:
Alerta de SLO ou regressão de bench
|
v
Reproduzir com carga / tamanho de payload realista
|
+-----+-----+
| |
v v
go test HTTP/gRPC
-bench carga + pprof
| |
+-----+-----+
v
Identificar func quente ou site de alocação
|
v
Uma mudança + gravado antes/depois
|
v
Enviar se o trade-off SLO/clareza for documentado
| Ferramenta | Pergunta que responde | Sinal Típico |
|---|---|---|
go test -bench | Esta função é mais rápida? | ns/op, allocs/op |
| pprof de CPU | Onde está o tempo de CPU? | Flat % em uma func |
| pprof de Heap | Quem aloca? | alloc_space, inuse_space |
go tool trace | Por que goroutines esperam? | Barras longas de syscall ou GC |
go build -gcflags=-m | O valor escapa? | Linhas moved to heap |
PGO (default.pgo) | Caminhos quentes em estado estável? | Dicas de inlining do compilador |
Benchmarks e perfis se complementam.
Um benchmark pode mostrar codificação JSON 30% mais rápida, enquanto um perfil de heap revela que a vitória veio de menos alocações que também reduziram a pausa do GC.
Sempre combine timing com -benchmem quando alocações são plausíveis.
Para serviços, exponha net/http/pprof em uma porta de admin ou puxe perfis com go tool pprof http://host:6060/debug/pprof/profile.
Colete perfis durante a carga, não em um processo ocioso, ou a amostra será ruído vazio.
Serviços sensíveis à latência (pagamentos, autenticação, APIs em tempo real) se importam com percentis de cauda, não com CPU média.
Traces expõem filas atrás de mutexes, backpressure de canais e segmentos STW do GC que as médias escondem.
Corrija contenção e alocação primeiro; só então experimente com GOGC ou GOMEMLIMIT.
Workers de throughput (ETL, indexadores em lote) frequentemente gargalam em IO.
Perfis de CPU podem mostrar domínio de syscall ou compress.
Paralelismo, dimensionamento de buffer e menos cópias superam a remoção de nanossegundos em funções hash.
Profile-Guided Optimization (PGO) alimenta perfis de CPU de produção no compilador via default.pgo.
Ajuda binários estáveis onde caminhos quentes não mudam a cada commit.
Regenere o perfil em branches de release, não de um trace de laptop de uso único.
Análise de escape explica por que uma struct pequena ainda aloca: retornar um ponteiro para um local, armazenar em um interface{}, ou capturar uma variável em um closure que sobrevive ao frame da stack.
Às vezes, a correção é retornar por valor; às vezes, é aceitar uma alocação como mais barata do que lutar contra o compilador.
Quando parar: SLOs estão verdes, grafos flat do pprof estão espalhados por muitas funções pequenas, e mudanças adicionais prejudicam a legibilidade.
Registre essa decisão no PR ou runbook para que o próximo engenheiro não reabra otimizações resolvidas.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Benchmarks | Rápido, amigável a CI | Risco de entrada sintética | Bibliotecas, codificadores |
| pprof | Verdade fundamental sob carga | Requer tráfego representativo | Serviços |
| Trace | Visibilidade do scheduler/GC | Artefatos maiores | Mistérios de latência de cauda |
| PGO | Compilador vê caminhos de produção | Desvio do perfil | Binários de longa duração |
| Knobs do GC | Experimentos rápidos | Mascaramento de bugs de alocação | Último recurso após correções de alocação |
go test -bench sozinho prova performance em produção - O ambiente de benchmark difere de caminhos de produção em rede, com contenção e em cache. Use-o para comparar implementações, depois valide com testes de carga.go tool trace quando o pprof de CPU estiver flat.go tool pprof -top e grafos de chama na UI web são suficientes para a maioria das primeiras passagens.Reproduza com payloads e concorrência em tamanho de produção.
Capture um perfil de CPU e um perfil de heap sob essa carga, depois leia as poucas funções de topo antes de editar o código.
Benchmarks ajudam helpers de handler e serializadores.
Latência de ponta a ponta requer testes de carga mais pprof ou tracing no servidor em execução.
Perfis de CPU mostram onde o tempo é gasto executando código.
Perfis de heap mostram sites de alocação e memória retida, que frequentemente impulsionam o custo do GC.
Use-o quando o uso de CPU parecer saudável, mas as requisições ainda enfileiram ou param.
Traces revelam agendamento, syscall e timing do GC em uma linha do tempo.
Green Tea GC é o coletor padrão em releases recentes, mas o loop medir-perfilar-otimizar não mudou.
Re-benchmark após atualizações da toolchain porque o comportamento do compilador e do runtime evolui.
Não.
Execute go build -gcflags=-m em um pacote quando um benchmark mostrar alocações inesperadas.
O compilador diz quais valores escapam para o heap.
Quando você envia binários de longa duração com caminhos quentes estáveis e quer que o compilador ineline e organize o código usando perfis de produção.
Pule PGO para caminhos de código de rápida movimentação ou ferramentas CLI com entradas diversas.
Geralmente não.
Menor pressão de alocação corrige a causa raiz; experimentos com GOGC são para latência após o trabalho de alocação ser esgotado.
Sim para benchmarks e guardas de regressão.
pprof completo sob carga pertence a staging ou janelas de produção controladas com autenticação em endpoints de debug.
Regras de Performance: Quando Otimizar é a folha de cola da política.
Esta seção é o guia de ferramentas prático para executar essa política.
Versões de Stack: Esta página foi escrita para Go 1.26.x (Green Tea GC padrão, go fix modernizers - verifique o patch na compilação), chi (última - verifique na compilação), gin (última - verifique na compilação), echo (última - verifique na compilação), google.golang.org/grpc (última - verifique na compilação), sigs.k8s.io/controller-runtime (última - verifique na compilação), kubebuilder (última - verifique na compilação), tinygo (última - verifique os alvos de placa na compilação), wazero (última - verifique na compilação), e golangci-lint (última - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026