Biblioteca Criterion para Rust: benchmarking estatístico
No mundo da programação de sistemas, desempenho não é apenas um desejo, mas muitas vezes um requisito rigoroso. Rust, sendo uma linguagem que dá ao desenvolvedor controle sobre memória e tempo de execução, exige ferramentas capazes de medir esse controle com precisão. É aqui que entra o Criterion — uma biblioteca para benchmarking estatístico que transforma o processo de medição de desempenho de um "cronômetro" bruto em um experimento científico.
Por que você precisa desta biblioteca? O benchmarking padrão do Rust (via #[bench]) é útil, mas tem uma falha fundamental: ele não leva em conta a variação estatística. Processadores modernos têm arquiteturas complexas com caches, preditores de desvio e turbo boost. O mesmo código pode executar em tempos diferentes dependendo do "ruído" do sistema operacional, temperatura da CPU ou estado aleatório do cache. O Criterion resolve esse problema executando cada teste várias vezes, coletando uma amostra dos tempos de execução e aplicando métodos estatísticos (média, mediana, desvio padrão, percentis). O resultado não é apenas "tempo em microssegundos", mas um intervalo de confiança que diz: "Com 95% de probabilidade, o tempo real de execução está entre X e Y".
Além disso, o Criterion oferece ferramentas poderosas para análise de regressão. Você pode comparar o desempenho de duas versões do seu código (por exemplo, antes e depois de uma otimização) e obter uma resposta estatisticamente significativa para a pergunta: "A nova versão é mais rápida ou a diferença está dentro da margem de erro?". A biblioteca gera relatórios HTML bonitos com gráficos, histogramas e tabelas, tornando a análise de desempenho visual e convincente. É uma ferramenta indispensável para desenvolvedores de bibliotecas, engines de jogos, bancos de dados, algoritmos criptográficos e qualquer outro código onde cada nanossegundo conta.
Instalação
A instalação do Criterion em um projeto Rust é feita através do gerenciador de pacotes cargo. Você precisa adicionar a dependência ao arquivo Cargo.toml do seu projeto. Recomenda-se adicioná-la como uma dependência de desenvolvimento, já que os benchmarks não fazem parte da aplicação final.
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "my_benchmark"
harness = false
Explicação:
version = "0.5"— especifica a versão da biblioteca. No momento da escrita, a versão atual é 0.5.x.features = ["html_reports"]— ativa a geração de relatórios HTML com gráficos. É um recurso opcional, mas muito útil.[[bench]]— seção que informa ao Cargo que temos um benchmark chamadomy_benchmark.harness = false— desativa o executor de benchmark integrado do Cargo. Isso é necessário para que o Criterion assuma o controle.
Depois disso, crie o arquivo benches/my_benchmark.rs (o caminho deve corresponder ao nome especificado em [[bench]]).
Início rápido: exemplo mínimo funcional
Vamos escrever um benchmark simples que mede a velocidade do cálculo da soma dos números de 1 a 1000.
// benches/my_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn sum_up_to(n: u64) -> u64 {
let mut sum = 0;
for i in 1..=n {
sum += i;
}
sum
}
fn bench_sum(c: &mut Criterion) {
// black_box impede a otimização do compilador
c.bench_function("sum 1000", |b| b.iter(|| sum_up_to(black_box(1000))));
}
criterion_group!(benches, bench_sum);
criterion_main!(benches);
Como executar:
cargo bench
Após a execução, você verá algo como no console: