Otimização de desempenho de aplicações Rust: para iniciantes

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que Rust já é rápido, mas este não é o limite

\n\n

Rust é merecidamente considerado uma das linguagens de programação de sistema mais performáticas. Graças às abstrações de custo zero (zero-cost abstractions) e ao sistema de propriedade rigoroso, o código em Rust geralmente é tão rápido quanto o código equivalente em C ou C++. No entanto, mesmo em Rust existem "armadilhas" que podem tornar a aplicação mais lenta, especialmente para desenvolvedores iniciantes.

\n\n

Neste artigo, vamos analisar os princípios-chave e as técnicas práticas de otimização de aplicações Rust. Você aprenderá como criar perfis de código, evitar alocações desnecessárias, trabalhar eficientemente com coleções e usar o poder do compilador. O material é voltado para iniciantes, portanto, todos os exemplos serão o mais simples e claros possível.

\n\n

Pronto para tornar seu código Rust ainda mais rápido? Vamos lá!

\n\n

1. Criação de perfil: primeiro medimos, depois otimizamos

\n\n

Antes de otimizar qualquer coisa, é preciso entender o que está causando lentidão. Em Rust, existem ferramentas excelentes para isso.

\n\n

1.1. Benchmarks com cargo bench

\n\n

A maneira padrão é usar benchmarks. Eles permitem medir o tempo de execução de funções específicas.

\n\n
// No arquivo benches/my_bench.rs\nuse criterion::{criterion_group, criterion_main, Criterion};\n\nfn fibonacci(n: u64) -> u64 {\n    match n {\n        0 => 1,\n        1 => 1,\n        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(20)));\n}\n\ncriterion_group!(benches, bench_fib);\ncriterion_main!(benches);\n
\n\n

Para executar, adicione ao Cargo.toml:

\n
[dev-dependencies]\ncriterion = "0.5"\n\n[[bench]]\nname = "my_bench"\nharness = false
\n\n

Em seguida, execute cargo bench. Você obterá números precisos: tempo de execução, desvio padrão e comparação com execuções anteriores.

\n\n

1.2. Criação de perfil de CPU e memória

\n\n

Para uma análise mais aprofundada, use:

\n
    \n
  • perf (Linux) — criador de perfil em nível de kernel.
  • \n
  • Valgrind — para encontrar vazamentos de memória e analisar o cache.
  • \n
  • flamegraph — visualização dos "pontos críticos" do código.
  • \n
\n\n

Exemplo de geração de flamegraph:

\n
cargo install flamegraph\ncargo flamegraph --bin my_app\n# Abra flamegraph.svg no navegador\n
\n\n

O Flamegraph mostrará quais funções consomem mais tempo. Isso ajudará a focar no que é mais importante.

\n\n

2. Gerenciamento de memória: evitando alocações desnecessárias

\n\n

Alocações no heap são uma das causas mais comuns de código lento. Rust oferece controle total sobre a memória, mas iniciantes frequentemente abusam de Box, Vec e String.

\n\n

2.1. Use a pilha em vez do heap

\n\n

Sempre que possível, armazene dados na pilha. Por exemplo, em vez de Vec para um conjunto fixo pequeno, use um array:

\n\n
// Lento: alocação no heap\nlet data: Vec<i32> = vec![1, 2, 3, 4, 5];\n\n// Rápido: pilha\nlet data: [i32; 5] = [1, 2, 3, 4, 5];\n
\n\n

2.2. Pré-alocação de memória

\n\n

Se o tamanho da coleção for conhecido antecipadamente, use with_capacity:

\n\n
// Ruim: muitas realocações\nlet mut v = Vec::new();\nfor i in 0..1000 {\n    v.push(i);\n}\n\n// Bom: uma alocação\nlet mut v = Vec::with_capacity(1000);\nfor i in 0..1000 {\n    v.push(i);\n}\n
\n\n

2.3. Evite clonagem

\n\n

Clonar strings e estruturas grandes é uma operação cara. Use empréstimo (&str em vez de String):

\n\n
fn process(s: &str) { /* ... */ }\n\n// Ruim: clonagem\nlet s = String::from("hello");\nprocess(&s.clone());\n\n// Bom: empréstimo\nprocess(&s);\n
\n\n

3. Uso de z

Blogs

Book Recommendations