---
title: "cargo-flamegraph em Rust: Profiling de Performance"
url: "https://rustlang.com.br/blog/cargo-flamegraph-rust-profiling-performance-2026/"
markdown_url: "https://rustlang.com.br/blog/cargo-flamegraph-rust-profiling-performance-2026.MD"
description: "Aprenda a usar cargo-flamegraph em Rust para encontrar gargalos de CPU. Guia com instalação, símbolos, benchmarks, leitura do gráfico e otimização segura."
date: "2026-08-24"
author: "Equipe Rust Brasil"
---

# cargo-flamegraph em Rust: Profiling de Performance

Aprenda a usar cargo-flamegraph em Rust para encontrar gargalos de CPU. Guia com instalação, símbolos, benchmarks, leitura do gráfico e otimização segura.


**Use `cargo-flamegraph` quando você já confirmou um problema de CPU e precisa descobrir quais funções e pilhas de chamadas consomem mais tempo.** Instale a ferramenta, compile com otimizações e símbolos de debug, reproduza uma carga representativa e abra o arquivo `flamegraph.svg`. Os frames mais largos concentram mais amostras; eles indicam onde investigar, não onde editar às cegas.

```bash
cargo install flamegraph --locked
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --release
```

Um flame graph complementa benchmarks e métricas de produção. Ele não responde sozinho se a aplicação está rápida o suficiente, nem mede corretamente uma espera de rede apenas por mostrar pilhas de CPU. O fluxo confiável é: estabelecer uma baseline, perfilar a mesma carga, formular uma hipótese, alterar uma coisa e medir novamente.

## Resposta rápida: fluxo recomendado

| Etapa | Comando ou ação | Objetivo |
|---|---|---|
| Instalar | `cargo install flamegraph --locked` | Adicionar o subcomando ao Cargo |
| Ver opções reais | `cargo flamegraph --help` | Confirmar flags da versão instalada |
| Gerar gráfico | `cargo flamegraph --release` | Perfilar o alvo padrão otimizado |
| Selecionar binário | `cargo flamegraph --bin minha-api --release` | Evitar medir o executável errado |
| Perfilar benchmark | `cargo flamegraph --bench parser -- --bench` | Exercitar uma carga CPU-bound específica |
| Preservar símbolos | `debug = true` e `strip = false` | Melhorar a identificação dos frames |
| Abrir resultado | navegador em `flamegraph.svg` | Explorar pilhas e funções quentes |
| Confirmar melhoria | `cargo bench` ou teste de carga | Separar ganho real de impressão visual |

Antes de automatizar comandos, confira a ajuda da versão homologada. Flags aceitas e backends disponíveis podem variar entre releases e sistemas operacionais.

## O que é um flame graph

Um flame graph é uma visualização agregada de pilhas de chamadas coletadas durante a execução. O profiler amostra periodicamente o programa e registra onde a CPU estava naquele instante. Depois, pilhas semelhantes são combinadas em retângulos.

A leitura básica é simples:

- o eixo horizontal representa a proporção de amostras, não uma linha do tempo;
- o eixo vertical representa a profundidade da pilha de chamadas;
- um frame sobre outro foi chamado pelo frame abaixo;
- quanto mais largo o frame, mais amostras passaram por aquela função ou por seus descendentes;
- as cores normalmente ajudam a distinguir frames, mas não significam automaticamente “bom”, “ruim”, “rápido” ou “lento”.

Duas torres afastadas no gráfico não necessariamente aconteceram em momentos separados. O desenho agrega stacks por nome e caminho. Para analisar uma sequência temporal, latência por requisição ou espera assíncrona, você pode precisar de tracing, métricas, spans ou outras ferramentas além do flame graph.

O guia de [profiling e performance em produção](/blog/rust-profiling-performance-producao-2026/) mostra como combinar essa visão com observabilidade. Já o artigo de [otimização de performance em Rust](/artigos/otimizacao-performance/) cobre técnicas que só devem ser aplicadas depois que o gargalo foi medido.

## Instalando no Linux e no macOS

Instale o subcomando pelo Cargo:

```bash
cargo install flamegraph --locked
cargo flamegraph --version
cargo flamegraph --help
```

No Linux, o fluxo normalmente depende do `perf`, fornecido pelos pacotes de ferramentas de performance da distribuição. O nome exato do pacote varia. Confirme primeiro:

```bash
perf --version
```

Se `perf` não estiver disponível, instale o pacote correspondente ao kernel e à distribuição usados. Em containers mínimos, talvez seja necessário perfilar no host ou conceder capacidades adicionais; abrir privilégios amplos em produção apenas para gerar um gráfico raramente é uma boa escolha.

O kernel Linux também pode restringir a coleta por meio de `perf_event_paranoid`. Não copie um comando que reduz a proteção global sem entender o ambiente. Prefira uma estação de profiling, uma VM de teste ou um runner controlado, com a menor permissão necessária e uma política aprovada pelo time.

No macOS, o backend de profiling e as permissões são diferentes. O sistema pode solicitar privilégios adicionais para observar processos. Execute `cargo flamegraph --help`, confira a documentação da versão instalada e faça a primeira coleta em um projeto local simples antes de depender do comando em uma pipeline.

## Compile com otimizações e símbolos úteis

Perfilar um build de debug quase sempre responde à pergunta errada. Código sem otimização pode ser dezenas de vezes mais lento, conter checks e chamadas que desaparecerão em release e produzir um hot path diferente do artefato entregue.

Crie um profile específico no `Cargo.toml`:

```toml
[profile.profiling]
inherits = "release"
debug = true
strip = false
```

Depois execute:

```bash
cargo flamegraph --profile profiling
```

Esse profile mantém otimizações de release e preserva informações úteis para simbolização. Uma alternativa pontual é:

```bash
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --release
```

Evite ativar `strip = true` no artefato usado para profiling. Também lembre que otimizações como inlining, monomorfização e LTO podem fundir ou remover funções. Isso não significa necessariamente que o profiler falhou; significa que o gráfico representa o código de máquina otimizado, não uma transcrição perfeita do código-fonte.

Se a aplicação usa um profile de produção personalizado, reproduza as mesmas opções importantes. Mudar simultaneamente `opt-level`, LTO, allocator, features e carga torna a comparação difícil de interpretar.

## Gerando o primeiro flame graph

Para um projeto com um único binário:

```bash
cargo flamegraph --release
```

Em um workspace ou pacote com vários alvos, seja explícito:

```bash
cargo flamegraph --package minha-api --bin servidor --release
```

Se o programa aceita argumentos, separe os argumentos do Cargo dos argumentos do executável com `--`:

```bash
cargo flamegraph --release -- processar --entrada dados.json
```

A saída padrão costuma ser um arquivo SVG no diretório atual. Abra-o no navegador e use a busca do próprio gráfico para localizar nomes de módulos, crates e funções.

Não gere o perfil com uma execução trivial se o problema aparece somente após aquecimento, com cache preenchido, lote grande ou concorrência. Prepare uma carga reproduzível. Por exemplo, para uma CLI de parsing:

```bash
cargo flamegraph --profile profiling -- \
  processar --entrada benches/dados/grandes.json
```

Para uma API, suba o servidor sob o profiler e gere tráfego de outra sessão. Mantenha taxa, duração, dataset e configuração registrados para repetir a coleta depois.

## Como ler o gráfico na prática

Comece pelos frames largos perto do topo e siga a pilha para baixo. Pergunte:

1. a função pertence ao código local, à biblioteca padrão, ao allocator, ao runtime ou a uma dependência?
2. o custo é esperado para a carga executada?
3. a função aparece porque faz trabalho útil ou porque é chamada vezes demais?
4. existe serialização, hashing, cópia, alocação ou conversão repetida dentro do hot path?
5. a pilha representa CPU ativa ou o problema real é espera, contenção ou I/O?

Imagine um serviço que recebe JSON, valida o payload e grava no banco. O gráfico pode mostrar `serde_json` largo. Isso não prova que trocar de biblioteca é a solução. Talvez o serviço esteja desserializando o mesmo conteúdo duas vezes, convertendo estruturas em `Value`, clonando strings ou registrando o payload completo em cada requisição.

Outro cenário comum é encontrar alocação e desalocação espalhadas por muitas pilhas. Antes de trocar o allocator, procure construções intermediárias, `format!` em loops, `collect()` desnecessário, clones e buffers sem capacidade inicial. Alterações locais e explícitas costumam ser mais fáceis de validar.

## Exemplo: encontrar trabalho repetido

Considere uma função que compila uma expressão regular dentro do processamento:

```rust
fn contar_ids(linhas: &[String]) -> usize {
    linhas
        .iter()
        .filter(|linha| {
            let regex = regex::Regex::new(r"ID-[0-9]{6}").unwrap();
            regex.is_match(linha)
        })
        .count()
}
```

Um flame graph de uma carga grande pode destacar compilação, parsing da expressão e alocações. A hipótese é mover a construção para fora do loop:

```rust
fn contar_ids(linhas: &[String]) -> usize {
    let regex = regex::Regex::new(r"ID-[0-9]{6}").unwrap();

    linhas
        .iter()
        .filter(|linha| regex.is_match(linha))
        .count()
}
```

Mas o gráfico não encerra a análise. Crie um benchmark com [Criterion](/ecossistema/criterion/) para comparar as duas implementações na mesma entrada. Se o ganho for relevante e os testes continuarem passando, a mudança tem evidência.

## cargo-flamegraph com benchmarks Criterion

Benchmark e profiler respondem perguntas diferentes. Criterion calcula distribuições de tempo, warm-up, amostras e comparação com baseline. O flame graph mostra o caminho interno usado durante a execução.

Um benchmark simples pode ser declarado assim:

```toml
[dev-dependencies]
criterion = "0.5"

[[bench]]
name = "parser"
harness = false
```

E perfilado com:

```bash
cargo flamegraph --bench parser -- --bench
```

O argumento final depende do harness e da versão das ferramentas. Confirme o comportamento com `cargo flamegraph --help`, `cargo bench --help` e uma execução curta.

Para benchmarks muito rápidos, o profiler pode coletar poucas amostras. Aumente o trabalho por iteração ou a duração da carga de forma controlada, sem misturar setup caro no trecho que você pretende investigar. O conteúdo sobre [testes e benchmarks em Rust](/blog/testes-rust-estrategias-boas-praticas-2026/) ajuda a separar correção, medição e diagnóstico.

## Async, Tokio e o risco de interpretar CPU como latência

Em aplicações Tokio, um flame graph pode mostrar executors, polling de futures, alocação, parsing e trabalho síncrono. Ele é útil para descobrir uma tarefa CPU-bound bloqueando uma worker thread, mas não é uma visão completa da latência assíncrona.

Se uma requisição passa a maior parte do tempo aguardando PostgreSQL, Redis ou uma API externa, o consumo de CPU pode ser pequeno. A largura dos frames não revelará por si só a duração da espera. Combine:

- flame graph para hot paths de CPU;
- [Tokio Console](/blog/tokio-console-debug-tasks-async-rust-2026/) para tarefas, polls e recursos assíncronos;
- [Tracing](/ecossistema/tracing/) para spans e causalidade;
- métricas de latência, throughput, erros e saturação;
- profiler do banco quando a query é o gargalo.

Também procure funções síncronas pesadas dentro de handlers async: compressão, hashing caro, parsing volumoso e loops CPU-bound podem ocupar a thread do runtime. Nesses casos, medir a pilha ajuda a justificar isolamento, batching ou paralelismo apropriado.

## Flame graph em container e produção

Perfilar dentro de container pode exigir acesso a recursos do kernel que a configuração padrão bloqueia. Não transforme `--privileged` em receita genérica. Uma abordagem mais segura é reproduzir a imagem e a carga em staging, usar uma máquina dedicada ou perfilar o processo a partir do host com permissões restritas.

Se o incidente só ocorre em produção:

1. confirme que a coleta é permitida pela política da empresa;
2. limite duração e impacto;
3. evite registrar argumentos, caminhos ou símbolos sensíveis em artefatos públicos;
4. preserve versão, commit, target e configuração da carga;
5. armazene o SVG com acesso controlado;
6. correlacione o período com métricas e traces.

Profilers por amostragem costumam ter overhead menor do que instrumentar cada chamada, mas “menor” não significa “zero”. Faça um teste de impacto antes de usar em serviço crítico.

## O que cargo-flamegraph não resolve

### Latência de rede e banco

Pouca CPU pode coexistir com resposta lenta. Use tracing e métricas de dependências.

### Vazamento ou pico de memória

O flame graph de CPU pode mostrar alocadores, mas não substitui um profiler de heap, métricas de RSS ou análise de retenção.

### Contenção entre threads

Locks podem aparecer indiretamente, porém uma ferramenta especializada em concorrência ou eventos do sistema pode ser necessária.

### Regressão estatística

A largura visual não é um teste de performance. Confirme com Criterion, hyperfine ou uma ferramenta de carga adequada.

### Código não exercitado

O profiler só vê caminhos executados. Uma carga pouco representativa produz um gráfico preciso da situação errada.

## Armadilhas comuns

### Perfilar em debug

Você encontra custos que não representam produção. Use um profile otimizado com símbolos.

### Escolher o frame mais largo e reescrevê-lo

O frame pode ser trabalho inevitável ou apenas o ponto onde custos de muitos chamadores se acumulam. Siga a pilha e entenda o contexto.

### Confundir cor com gravidade

No flame graph tradicional, a cor não é uma escala universal de lentidão. A largura é o sinal principal.

### Trocar várias coisas ao mesmo tempo

Alterar allocator, estrutura de dados, paralelismo e flags de compilação impede saber qual mudança produziu o resultado.

### Comparar cargas diferentes

Um gráfico com mil itens e outro com um milhão não formam uma comparação útil sem normalização e contexto.

### Ignorar símbolos ausentes

Endereços e frames desconhecidos reduzem a capacidade de diagnóstico. Verifique `debug`, `strip`, bibliotecas nativas e simbolização.

### Otimizar uma dependência sem revisar o uso

Às vezes a crate aparece larga porque o código a chama repetidamente ou usa uma API mais genérica do que precisa. Investigue o padrão de chamada antes de substituir a biblioteca.

## Colocando profiling no fluxo da equipe

Um processo simples e repetível vale mais do que um SVG isolado:

1. registre a métrica problemática: p95, throughput, CPU por operação ou duração do lote;
2. salve uma baseline com a carga e o commit;
3. gere o flame graph no mesmo ambiente;
4. escreva uma hipótese específica;
5. implemente a menor mudança capaz de testá-la;
6. execute testes de correção;
7. repita benchmark e profiling;
8. documente ganho, custo e trade-offs;
9. reverta se não houver melhoria consistente.

Em equipes com CI, o gráfico pode ser publicado como artefato de um job manual. Evite falhar pull requests porque um frame ficou visualmente maior. Runners compartilhados sofrem interferência de CPU, frequência, vizinhos e virtualização. Para gates, use uma infraestrutura de benchmark controlada, tolerâncias definidas e histórico suficiente para distinguir ruído de regressão.

## Checklist antes de otimizar

- [ ] O problema foi confirmado por uma métrica real.
- [ ] A carga reproduz o cenário relevante.
- [ ] O binário usa otimizações comparáveis às de produção.
- [ ] Símbolos de debug estão disponíveis e `strip` está desativado.
- [ ] O alvo correto do workspace foi selecionado.
- [ ] O gráfico possui amostras suficientes.
- [ ] A pilha foi analisada, não apenas o frame mais largo.
- [ ] CPU foi distinguida de espera por I/O.
- [ ] Uma hipótese pequena foi registrada.
- [ ] Criterion ou teste de carga confirmará o resultado.
- [ ] Testes funcionais protegem contra regressões de correção.
- [ ] O artefato não será publicado com dados sensíveis.

## cargo-flamegraph para portfólio e entrevistas

Um projeto de portfólio fica mais convincente quando mostra uma investigação completa. Em vez de escrever “otimizei o parser”, apresente:

- dataset e comando reproduzível;
- baseline de tempo e throughput;
- flame graph anterior;
- hipótese encontrada na pilha;
- mudança pequena no código;
- benchmark posterior com distribuição;
- trade-off de memória, legibilidade ou complexidade;
- gráfico posterior como evidência complementar.

Esse material demonstra uma habilidade valorizada em [vagas Rust](/vagas/) de backend, infraestrutura, bancos de dados, embedded, engines, segurança e developer tooling: tomar decisões de performance a partir de evidências. Também mostra que você sabe evitar otimização prematura e preservar correção.

## Perguntas frequentes

### Para que serve cargo-flamegraph?

Ele gera uma visualização agregada das pilhas de chamadas amostradas durante a execução de um programa Rust, ajudando a localizar caminhos que concentram CPU.

### Qual comando gera o gráfico?

O começo mais comum é:

```bash
cargo install flamegraph --locked
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --release
```

Ajuste pacote, binário, profile e argumentos para o projeto real.

### Frame largo significa código ruim?

Não. Significa que muitas amostras passaram por aquele frame ou seus descendentes. O custo pode ser esperado, inevitável ou causado por quem chama a função.

### Devo usar Criterion ou cargo-flamegraph?

Use os dois: Criterion mede e compara; cargo-flamegraph ajuda a explicar onde a CPU foi consumida.

### Funciona com Tokio?

Sim, mas CPU profiling não representa sozinho esperas assíncronas. Combine com Tokio Console, tracing e métricas de dependências.

## Conclusão

`cargo-flamegraph` é uma das formas mais práticas de transformar “minha aplicação Rust está usando muita CPU” em uma investigação concreta. Ele mostra pilhas quentes, revela trabalho repetido e ajuda a direcionar benchmarks para a parte certa do sistema.

A sequência recomendada é: **meça o problema, compile com otimizações e símbolos, reproduza a carga, leia a pilha completa, formule uma hipótese e confirme a mudança com benchmark**. Não trate o gráfico como ranking automático de funções ruins e não use CPU profiling para explicar toda forma de latência.

Comece com uma carga pequena, porém representativa:

```bash
cargo install flamegraph --locked
cargo flamegraph --profile profiling
```

Abra `flamegraph.svg`, escolha um caminho largo que pertença ao cenário medido e investigue por que ele existe. A melhor otimização não é a que deixa o gráfico mais bonito; é a que melhora uma métrica relevante sem quebrar correção, operação ou manutenção.
