Por que Rust é rápido, mas nem sempre?
\nRust é uma linguagem que promete a performance do C++ com segurança de memória. Mas mesmo em Rust é possível escrever código lento. Algumas decisões erradas — e seu serviço começa a "travar" sob carga. A boa notícia: Rust fornece todas as ferramentas para encontrar e eliminar gargalos. Neste artigo, vamos abordar como perfilar código Rust e quais técnicas de otimização realmente funcionam.
\n\nNão falaremos de forma abstrata. Você verá exemplos reais de código, aprenderá a usar perf, flamegraph, valgrind e benchmarks integrados. Após a leitura, você poderá realizar uma auditoria de performance do seu projeto de forma independente.
Ferramentas para Perfilamento Rust
\nAntes de otimizar algo, é preciso entender o que está travando. Aqui estão três ferramentas práticas que ajudarão você a olhar dentro do programa.
\n\n1. Benchmarks com cargo bench
\nA maneira padrão de medir performance é escrever um benchmark. Em Rust, usa-se a crate criterion ou o framework de teste integrado. Exemplo:
use criterion::{black_box, criterion_group, criterion_main, Criterion};\n\nfn fibonacci(n: u64) -> u64 {\n match n {\n 0 => 0,\n 1 => 1,\n _ => fibonacci(n - 1) + fibonacci(n - 2),\n }\n}\n\nfn bench_fib(c: &mut Criterion) {\n c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));\n}\n\ncriterion_group!(benches, bench_fib);\ncriterion_main!(benches);\n\n\nExecução: cargo bench. Como resultado, você obterá o tempo de execução, desvio e comparação com execuções anteriores. Esta é a base para iniciar qualquer otimização.
2. Perfilamento com perf e flamegraph
\nQuando precisar saber quais funções consomem a CPU, use perf. Colete os dados e depois visualize-os na forma de um flamegraph:
# Coleta de dados\nperf record -g ./target/release/my_app\n# Geração do flamegraph\nperf script | stackcollapse-perf.pl | flamegraph.pl > profile.svg\n\n\nO flamegraph mostrará as partes "quentes" do código. Quanto mais larga a barra, mais tempo é gasto naquela função. Esta é a maneira mais visual de encontrar um gargalo.
\n\n3. Busca por vazamentos com Valgrind
\nRust protege contra vazamentos de memória em nível de compilação, mas não contra todos. Por exemplo, se você usar Rc ou Arc com referências cíclicas, a memória pode não ser liberada. O Valgrind ajuda a detectar isso:
valgrind --tool=massif ./target/debug/my_app\n\n\nO Massif mostrará quanta memória o programa consome a cada momento. Se o gráfico não cair após a conclusão da execução — procure um vazamento.
\n\nTécnicas Práticas de Otimização de Código em Rust
\nAgora que você sabe o que procurar, vamos ver como corrigir. Aqui estão cinco métodos comprovados que realmente aceleram o código.
\n\n1. Abstrações de custo zero: use iteradores corretamente
\nIteradores em Rust são compilados para um código tão eficiente quanto loops manuais. Mas apenas se você não fizer cópias desnecessárias. Compare:
\n\n// Ruim: cria Vec, copia, filtra\nlet result: Vec<u32> = data.iter().filter(|x| x % 2 == 0).collect();\n// Bom: trabalha com o iterador sem coleções intermediárias\nlet sum: u32 = data.iter().filter(|x| x % 2 == 0).sum();\n\n\nA segunda opção não aloca memória para um vetor temporário. Se você precisa apenas de um valor — não colete.
\n\n2. Cache de resultados
\nSe uma função é chamada repetidamente com os mesmos argumentos, armazene o resultado em cache. Use HashMap ou a crate lru para limitar o tamanho do cache:
use std::collections::HashMap;\n\nfn expensive_calc(x: u64) -> u64 {\n // ... cálculos longos\n x * x\n}\n\nfn c