Noções Básicas de Desempenho
10 exemplos para você começar com Desempenho - 7 básicos e 3 intermediários.
Pré-requisitos
- Rust 1.97.0 (edição 2024)
- Builds de lançamento para tempos significativos:
cargo build --release
Exemplos Básicos
1. Meça Antes de Otimizar
Faça perfil ou benchmark antes de alterar o código.
cargo build --release
cargo bench # se houver benches Criterion
# ou: perf record / samply para perfis de processo completo- Builds de depuração são 10-100x mais lentos que os de lançamento
- Adivinhar hotspots desperdiça tempo
- Documente a linha de base antes das alterações
- Meça novamente após cada otimização
Relacionado: Benchmarking e Profiling
2. Padrões de Perfil de Lançamento
Envie com binários otimizados.
[profile.release]
opt-level = 3
lto = "thin"cargo run --releaseopt-level = 3habilita otimizações LLVM- LTO habilita inlining entre crates
- Perfis de desenvolvimento vs. lançamento separadamente
- Nunca ajuste algoritmos apenas em depuração
Relacionado: Ajuste de Lançamento
3. Abstrações de Custo Zero
Iteradores e genéricos compilam para loops compactos.
let sum: u64 = (0..1_000_000).map(|x| x as u64).sum();- Código de alto nível pode corresponder a loops escritos manualmente
- Inspecione o assembly com
cargo asmou godbolt se tiver dúvidas - Prefira iteradores até que o profiling diga o contrário
- Clareza primeiro, micro-otimização em segundo
Relacionado: Abstrações de Custo Zero
4. Evite Alocações Desnecessárias
Pilha e reutilização superam a agitação do heap.
let mut buf = String::with_capacity(256);
buf.clear(); // reutilizar no loopwith_capacityevita realocações repetidas&strem vez deStringquando o empréstimo é suficiente- Observe
clone()em loops quentes cargo clippysinaliza clones redundantes
Relacionado: Entendendo Alocações
5. Empreste em Vez de Clonar
Cópias de propriedade são explícitas e custosas.
fn process(items: &[Item]) {
for item in items {
use_item(item);
}
}&Te&[T]evitam cópias profundasCow<'_, str>quando mutação rara é necessária- Projete APIs para aceitar referências
- Clone apenas nas fronteiras de persistência
Relacionado: Evitando Clones
6. Use Coleções Apropriadas
Escolha estruturas para padrões de acesso.
use std::collections::HashMap;
let mut index: HashMap<u64, User> = HashMap::with_capacity(1024);Vecpara acesso sequencialHashMap/BTreeMappara consultasVecDequepara padrões de fila- Pré-dimensionar com
with_capacityquando o tamanho for conhecido
7. Dicas de Inlining com Moderação
#[inline] ajuda caminhos quentes entre crates.
#[inline]
pub fn cheap_op(x: u32) -> u32 {
x.wrapping_mul(31)
}- O compilador faz inline na maioria das funções pequenas em builds LTO
#[inline]em pequenos helpers públicos em bibliotecas- Não espalhe por tudo
- Faça perfil antes de adicionar atributos inline
Exemplos Intermediários
8. Layout de Dados Amigável ao Cache
Ordene os campos para localidade.
#[repr(C)]
struct Particle {
x: f32,
y: f32,
active: bool,
}- SoA vs AoS para loops quentes
repr(C)para layout previsível com FFI- Veja o guia de layout de memória para padrões ECS
Relacionado: Layout de Memória e Cache
9. SIMD e Vetorização
Deixe o LLVM vetorizar ou use SIMD explícito.
let sum: u32 = data.iter().copied().sum();- Cadeias de iteradores simples geralmente são auto-vetorizadas em
-O3 std::simdpara kernels ajustados manualmente- Faça benchmark antes de complexidade SIMD manual
Relacionado: SIMD e Vetorização
10. Tamanho Binário vs. Velocidade
Ajuste para restrições de implantação.
[profile.release]
opt-level = "z"
strip = "symbols"- Ferramentas embarcadas e CLI podem favorecer o tamanho
- Servidores geralmente favorecem a taxa de transferência
- Meça tanto a latência quanto os MB binários
Relacionado: Reduzindo o Tamanho Binário
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.