---
title: "Otimização de Performance em Rust: Guia | Rust Brasil"
url: "https://rustlang.com.br/artigos/otimizacao-performance/"
markdown_url: "https://rustlang.com.br/artigos/otimizacao-performance.MD"
description: "Guia de otimização de performance em Rust: benchmarks, profiling, SIMD, cache-friendly code e dicas práticas."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Otimização de Performance em Rust: Guia | Rust Brasil

Guia de otimização de performance em Rust: benchmarks, profiling, SIMD, cache-friendly code e dicas práticas.


## Introdução

Rust é famosa por oferecer performance próxima a C e C++, mas escrever código Rust não garante automaticamente código rápido. A linguagem fornece as ferramentas e abstrações para alcançar alta performance, mas cabe ao desenvolvedor usá-las corretamente.

O princípio fundamental da otimização é: **meça antes de otimizar**. Otimizações prematuras baseadas em intuição frequentemente pioram a legibilidade do código sem ganho real de performance. Neste artigo, vamos explorar como identificar gargalos, aplicar otimizações comprovadas e usar as ferramentas do ecossistema Rust para extrair o máximo de desempenho do seu código.

## O Problema: Otimização às Cegas

Muitos desenvolvedores cometem erros de performance sem perceber, ou otimizam as partes erradas do código.

### Não Faça Isso: Alocações Desnecessárias

```rust
// ERRADO: Aloca uma nova String a cada iteração
fn processar_nomes(nomes: &[String]) -> Vec<String> {
    let mut resultado = Vec::new();
    for nome in nomes {
        // .to_uppercase() cria uma nova String a cada chamada
        let formatado = format!("Sr(a). {}", nome.to_uppercase());
        resultado.push(formatado);
    }
    resultado
}

// ERRADO: Concatenação com + aloca repetidamente
fn construir_relatorio(itens: &[String]) -> String {
    let mut relatorio = String::new();
    for item in itens {
        relatorio = relatorio + item + "\n"; // Nova alocação a cada +
    }
    relatorio
}
```

### Não Faça Isso: Clonar sem Necessidade

```rust
// ERRADO: Clona o HashMap inteiro para uma simples busca
fn buscar_usuario(
    banco: &std::collections::HashMap<u64, String>,
    id: u64,
) -> Option<String> {
    let copia = banco.clone(); // Clona TUDO para buscar UM item
    copia.get(&id).cloned()
}

// ERRADO: Clona a struct inteira para acessar um campo
fn obter_nome(usuario: &Usuario) -> String {
    let copia = usuario.clone();
    copia.nome // Poderia simplesmente retornar uma referência
}

#[derive(Clone)]
struct Usuario {
    nome: String,
    email: String,
}
```

## A Solução: Profile-Driven Optimization

### Passo 1: Benchmarks com Criterion

Antes de otimizar, estabeleça uma baseline mensurável:

```toml
# Cargo.toml
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }

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

```rust
// benches/meus_benchmarks.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn soma_iterador(dados: &[i64]) -> i64 {
    dados.iter().sum()
}

fn soma_loop(dados: &[i64]) -> i64 {
    let mut total = 0i64;
    for &valor in dados {
        total += valor;
    }
    total
}

fn soma_fold(dados: &[i64]) -> i64 {
    dados.iter().fold(0i64, |acc, &x| acc + x)
}

fn benchmark_somas(c: &mut Criterion) {
    let dados: Vec<i64> = (0..10_000).collect();

    let mut group = c.benchmark_group("somas");
    group.bench_function("iterador", |b| {
        b.iter(|| soma_iterador(black_box(&dados)))
    });
    group.bench_function("loop", |b| {
        b.iter(|| soma_loop(black_box(&dados)))
    });
    group.bench_function("fold", |b| {
        b.iter(|| soma_fold(black_box(&dados)))
    });
    group.finish();
}

criterion_group!(benches, benchmark_somas);
criterion_main!(benches);
```

Execute com `cargo bench` para obter medições estatisticamente precisas.

### Passo 2: Profiling com Flamegraph

Instale e use `flamegraph` para identificar onde o tempo é gasto:

```bash
# Instalar
cargo install flamegraph

# Gerar flamegraph (Linux)
cargo flamegraph --bin meu_programa

# Para benchmarks específicos
cargo flamegraph --bench meus_benchmarks -- --bench
```

No `Cargo.toml`, habilite símbolos de debug mesmo em release:

```toml
[profile.release]
debug = true  # Permite profiling com nomes de funções
```

## Faça Isso: Otimizações Comprovadas

### String vs &str — Evite Alocações

```rust
// ERRADO: Aceita String, forçando o chamador a alocar
fn saudacao(nome: String) -> String {
    format!("Olá, {nome}!")
}

// CORRETO: Aceita &str, funciona com String e &str sem alocação
fn saudacao(nome: &str) -> String {
    format!("Olá, {nome}!")
}

fn main() {
    // Ambos funcionam sem custo extra:
    println!("{}", saudacao("Mundo"));             // &str direto
    println!("{}", saudacao(&String::from("Rust"))); // &String → &str
}
```

Para maior flexibilidade, use `impl AsRef<str>`:

```rust
fn saudacao(nome: impl AsRef<str>) -> String {
    format!("Olá, {}!", nome.as_ref())
}
```

### Vec::with_capacity — Pré-aloque Quando Souber o Tamanho

```rust
// ERRADO: Vec cresce dinamicamente, realocando múltiplas vezes
fn gerar_quadrados(n: usize) -> Vec<u64> {
    let mut resultado = Vec::new(); // Capacidade inicial = 0
    for i in 0..n {
        resultado.push((i as u64) * (i as u64));
    }
    resultado
}

// CORRETO: Uma única alocação
fn gerar_quadrados(n: usize) -> Vec<u64> {
    let mut resultado = Vec::with_capacity(n);
    for i in 0..n {
        resultado.push((i as u64) * (i as u64));
    }
    resultado
}

// MELHOR: Use iteradores — o compilador otimiza automaticamente
fn gerar_quadrados(n: usize) -> Vec<u64> {
    (0..n as u64).map(|i| i * i).collect()
}
```

### Iteradores vs Loops — Deixe o Compilador Otimizar

Iteradores em Rust são abstrações de custo zero (*zero-cost abstractions*). O compilador os transforma em código equivalente a loops manuais otimizados:

```rust
// Funcional com iteradores — idiomático e rápido
fn processar_vendas(vendas: &[f64]) -> f64 {
    vendas
        .iter()
        .filter(|&&v| v > 100.0)      // Filtra vendas acima de R$100
        .map(|&v| v * 0.1)             // Calcula 10% de comissão
        .sum()                          // Soma tudo
}

// Equivalente com loop — mesma performance, menos idiomático
fn processar_vendas_loop(vendas: &[f64]) -> f64 {
    let mut total = 0.0;
    for &v in vendas {
        if v > 100.0 {
            total += v * 0.1;
        }
    }
    total
}
```

Ambas as versões geram código de máquina praticamente idêntico, mas a versão com iteradores é mais composível e menos propensa a erros.

### Evite Alocações em Loops

```rust
use std::fmt::Write;

// ERRADO: Aloca uma nova String a cada iteração com format!
fn gerar_csv_ruim(dados: &[(String, f64)]) -> String {
    let mut csv = String::new();
    for (nome, valor) in dados {
        csv += &format!("{nome},{valor}\n"); // Aloca e descarta a cada iteração
    }
    csv
}

// CORRETO: Usa write! diretamente no buffer existente
fn gerar_csv_bom(dados: &[(String, f64)]) -> String {
    let mut csv = String::with_capacity(dados.len() * 30); // Estimativa
    for (nome, valor) in dados {
        writeln!(csv, "{nome},{valor}").unwrap();
    }
    csv
}
```

### Cow — Clone on Write para Flexibilidade

```rust
use std::borrow::Cow;

// Cow evita alocação quando não é necessária
fn normalizar_nome(nome: &str) -> Cow<'_, str> {
    if nome.contains(char::is_uppercase) {
        // Somente aloca quando precisa transformar
        Cow::Owned(nome.to_lowercase())
    } else {
        // Sem alocação — retorna referência ao original
        Cow::Borrowed(nome)
    }
}

fn main() {
    let n1 = normalizar_nome("maria");  // Cow::Borrowed — zero alocação
    let n2 = normalizar_nome("MARIA");  // Cow::Owned — aloca apenas aqui
    println!("{n1}, {n2}");
}
```

## Flags do Compilador para Performance

### Cargo.toml — Profile de Release Otimizado

```toml
[profile.release]
opt-level = 3          # Otimização máxima
lto = "fat"            # Link-Time Optimization agressiva
codegen-units = 1      # Compilação single-threaded para melhor otimização
panic = "abort"        # Menor binário (sem unwind tables)
strip = true           # Remove símbolos de debug

[profile.release-with-debug]
inherits = "release"
debug = true           # Para profiling
strip = false
```

**Impacto típico:**

| Flag | Impacto em Performance | Impacto na Compilação |
|---|---|---|
| `lto = "fat"` | +10-20% mais rápido | 2-3x mais lento |
| `codegen-units = 1` | +5-10% mais rápido | 2x mais lento |
| `opt-level = 3` | Máxima otimização | Mais lento |
| `panic = "abort"` | Binário menor | Neutro |

### Target-Specific Optimizations

```bash
# Compilar para a CPU específica da máquina
RUSTFLAGS="-C target-cpu=native" cargo build --release

# Verificar quais features da CPU estão disponíveis
rustc --print target-features
```

## Armadilhas Comuns

### 1. Medir em Modo Debug

```bash
# ERRADO: cargo run compila em modo debug (sem otimizações)
cargo run

# CORRETO: Sempre use --release para benchmarks
cargo run --release
```

A diferença pode ser de **10-100x** entre debug e release.

### 2. Usar `collect()` Intermediários

```rust
// ERRADO: Coleta intermediária desnecessária
let resultado: i64 = dados
    .iter()
    .filter(|&&x| x > 0)
    .collect::<Vec<_>>()  // Alocação desnecessária!
    .iter()
    .map(|&&x| x * 2)
    .sum();

// CORRETO: Chain sem intermediários
let resultado: i64 = dados
    .iter()
    .filter(|&&x| x > 0)
    .map(|&x| x * 2)
    .sum();
```

### 3. HashMap quando Vec Basta

```rust
use std::collections::HashMap;

// ERRADO: HashMap para poucas entradas indexadas por inteiro
let mut mapa: HashMap<usize, String> = HashMap::new();
for i in 0..100 {
    mapa.insert(i, format!("item_{i}"));
}

// CORRETO: Vec é muito mais rápido para acesso por índice
let vetor: Vec<String> = (0..100).map(|i| format!("item_{i}")).collect();
// vetor[42] é O(1) sem hashing
```

### 4. Serialização/Desserialização em Hot Path

```rust
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Evento {
    tipo: String,
    timestamp: u64,
    dados: Vec<u8>,
}

// ERRADO: Serializar/desserializar JSON em um loop quente
fn processar_eventos_ruim(eventos_json: &[String]) -> usize {
    let mut total = 0;
    for json_str in eventos_json {
        let evento: Evento = serde_json::from_str(json_str).unwrap();
        total += evento.dados.len();
    }
    total
}

// MELHOR: Desserializar uma vez, processar em memória
fn processar_eventos_bom(eventos: &[Evento]) -> usize {
    eventos.iter().map(|e| e.dados.len()).sum()
}
```

## Exemplos do Mundo Real

### Processamento de Dados em Lote

```rust
use std::io::{self, BufRead, BufWriter, Write};
use std::fs::File;

fn processar_arquivo_grande(entrada: &str, saida: &str) -> io::Result<()> {
    let arquivo_entrada = File::open(entrada)?;
    let leitor = io::BufReader::with_capacity(64 * 1024, arquivo_entrada); // Buffer de 64KB

    let arquivo_saida = File::create(saida)?;
    let mut escritor = BufWriter::with_capacity(64 * 1024, arquivo_saida);

    for linha in leitor.lines() {
        let linha = linha?;
        if let Some((chave, valor)) = linha.split_once(',') {
            if let Ok(num) = valor.trim().parse::<f64>() {
                if num > 1000.0 {
                    writeln!(escritor, "{chave},{num}")?;
                }
            }
        }
    }

    escritor.flush()?;
    Ok(())
}
```

As técnicas aplicadas aqui incluem:
- **BufReader/BufWriter** com buffer grande para reduzir syscalls
- **split_once** em vez de `split().collect()` para evitar alocação
- **Processamento streaming** em vez de carregar tudo na memória

### Paralelismo com Rayon

```toml
# Cargo.toml
[dependencies]
rayon = "1"
```

```rust
use rayon::prelude::*;

fn calcular_media_paralela(dados: &[f64]) -> f64 {
    let soma: f64 = dados.par_iter().sum(); // Paraleliza automaticamente
    soma / dados.len() as f64
}

fn processar_imagens(caminhos: &[String]) -> Vec<u64> {
    caminhos
        .par_iter()
        .map(|caminho| {
            // Cada imagem é processada em uma thread diferente
            let bytes = std::fs::read(caminho).unwrap_or_default();
            bytes.len() as u64
        })
        .collect()
}
```

Basta trocar `.iter()` por `.par_iter()` para paralelizar operações CPU-bound com o Rayon.

## Checklist de Otimização

1. **Meça primeiro** — Use `criterion` ou `flamegraph` antes de qualquer mudança
2. **Compile em release** — Nunca meça performance em modo debug
3. **Pré-aloque** — `Vec::with_capacity`, `String::with_capacity`
4. **Prefira referências** — `&str` sobre `String`, `&[T]` sobre `Vec<T>`
5. **Use iteradores** — Composíveis, legíveis e zero-cost
6. **Minimize alocações em loops** — Reutilize buffers
7. **Ative LTO** — `lto = "fat"` para builds de release
8. **Considere `Cow`** — Quando a alocação é condicional
9. **Use Rayon** — Para paralelizar operações CPU-bound
10. **Perfil target-cpu** — `RUSTFLAGS="-C target-cpu=native"` para a máquina alvo

---

## Veja Também

- [Receita: Medir Tempo de Execução](/receitas/medir-tempo-execucao/) — Como medir a performance do seu código
- [Boas Práticas de Error Handling](/artigos/boas-praticas-error-handling/) — Trate erros sem sacrificar performance
- [CI/CD para Projetos Rust](/artigos/ci-cd-rust/) — Automatize benchmarks no pipeline
- [Logging e Observabilidade](/artigos/logging-observabilidade/) — Monitore a performance em produção
- [Rust vs Go: Qual Escolher](/artigos/rust-vs-go/) — Comparação de performance entre Rust e Go
- [Zig Brasil](https://ziglang.com.br) — Zig é outra linguagem focada em performance de sistemas — compare as abordagens
