Como fazer benchmark de c贸digo Rust com libtest bench


O que 茅 Benchmarking?

Benchmarking 茅 a pr谩tica de testar o desempenho do seu c贸digo para ver qu茫o r谩pido (lat锚ncia) ou quanto (throughput) trabalho ele pode executar. Este passo frequentemente negligenciado no desenvolvimento de software 茅 crucial para criar e manter um c贸digo r谩pido e perform谩tico. O benchmarking fornece as m茅tricas necess谩rias para que os desenvolvedores compreendam o desempenho do seu c贸digo sob v谩rias cargas de trabalho e condi莽玫es. Pelas mesmas raz玫es que voc锚 escreve testes unit谩rios e de integra莽茫o para evitar regress玫es de funcionalidades, voc锚 deve escrever benchmarks para evitar regress玫es de desempenho. Bugs de desempenho s茫o bugs!

Escreva FizzBuzz em Rust

Para escrevermos testes de desempenho, precisamos de algum c贸digo-fonte para testar. Para come莽ar, vamos escrever um programa muito simples, FizzBuzz.

As regras para o FizzBuzz s茫o as seguintes:

Escreva um programa que imprima os inteiros de 1 a 100 (inclusive):

  • Para m煤ltiplos de tr锚s, imprima Fizz
  • Para m煤ltiplos de cinco, imprima Buzz
  • Para m煤ltiplos de tr锚s e cinco, imprima FizzBuzz
  • Para todos os outros, imprima o n煤mero

Existem muitas maneiras de escrever o FizzBuzz. Ent茫o vamos seguir com o meu favorito:

fn main() {
for i in 1..=100 {
match (i % 3, i % 5) {
(0, 0) => println!("FizzBuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
(_, _) => println!("{i}"),
}
}
}
  • Crie uma fun莽茫o main
  • Itere de 1 a 100 inclusivamente.
  • Para cada n煤mero, calcule o m贸dulo (resto depois da divis茫o) para ambos 3 e 5.
  • Fa莽a correspond锚ncia de padr玫es nos dois restos. Se o resto 茅 0, ent茫o o n煤mero 茅 m煤ltiplo do fator dado.
  • Se o resto 茅 0 para ambos 3 e 5 ent茫o imprima FizzBuzz.
  • Se o resto 茅 0 apenas para 3 ent茫o imprima Fizz.
  • Se o resto 茅 0 apenas para 5 ent茫o imprima Buzz.
  • Caso contr谩rio, apenas imprima o n煤mero.

Siga Passo a Passo

Para acompanhar este tutorial passo a passo, voc锚 precisa instalar Rust.

馃惏 O c贸digo fonte para esta postagem est谩 dispon铆vel no GitHub

Com Rust instalado, voc锚 pode ent茫o abrir uma janela de terminal e digitar: cargo init game

Em seguida, navegue para o diret贸rio game rec茅m-criado.

game
鈹溾攢鈹 Cargo.toml
鈹斺攢鈹 src
鈹斺攢鈹 main.rs

Voc锚 ver谩 um diret贸rio chamado src com um arquivo chamado main.rs:

fn main() {
println!("Hello, world!");
}

Substitua o conte煤do dele pela implementa莽茫o FizzBuzz acima. Depois, execute cargo run. A sa铆da deve ser parecida com:

$ cargo run
Compiling playground v0.0.1 (/home/bencher)
Finished dev [unoptimized + debuginfo] target(s) in 0.44s
Running `target/debug/game`
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...
97
98
Fizz
Buzz

馃惏 Boom! Voc锚 est谩 arrasando na entrevista de programa莽茫o!

Um novo arquivo Cargo.lock deve ter sido gerado:

game
鈹溾攢鈹 Cargo.lock
鈹溾攢鈹 Cargo.toml
鈹斺攢鈹 src
鈹斺攢鈹 main.rs

Antes de prosseguir, 茅 importante discutir as diferen莽as entre micro-benchmarking e macro-benchmarking.

Micro-Benchmarking vs Macro-Benchmarking

Existem duas categorias importantes de benchmarks de software: micro-benchmarks e macro-benchmarks. Os micro-benchmarks operam em um n铆vel semelhante aos testes unit谩rios. Por exemplo, um benchmark para uma fun莽茫o que determina Fizz, Buzz, ou FizzBuzz para um n煤mero individual seria um micro-benchmark. Os macro-benchmarks operam em um n铆vel semelhante aos testes de integra莽茫o. Por exemplo, um benchmark para uma fun莽茫o que executa o jogo inteiro de FizzBuzz, de 1 a 100, seria um macro-benchmark.

Em geral, 茅 melhor testar no menor n铆vel de abstra莽茫o poss铆vel. No caso dos benchmarks, isso os torna mais f谩ceis de manter, e ajuda a reduzir a quantidade de ru铆do nas medi莽玫es. No entanto, assim como ter alguns testes de ponta a ponta pode ser muito 煤til para verificar se todo o sistema se junta conforme esperado, ter macro-benchmarks pode ser muito 煤til para garantir que os caminhos cr铆ticos atrav茅s do seu software permane莽am com bom desempenho.

Benchmarking em Rust

As tr锚s op莽玫es populares para benchmarking em Rust s茫o: libtest bench, Criterion, e Iai.

libtest 茅 o framework embutido de testes unit谩rios e benchmarking do Rust. Embora fa莽a parte da biblioteca padr茫o do Rust, o libtest bench ainda 茅 considerado inst谩vel, portanto, est谩 dispon铆vel apenas em vers玫es do compilador nightly. Para funcionar no compilador Rust est谩vel, um harness de benchmarking separado precisa ser usado. Nenhum dos dois est谩 sendo desenvolvido ativamente, no entanto.

O harness de benchmarking mais popular dentro do ecossistema Rust 茅 o Criterion. Ele funciona tanto em vers玫es est谩veis quanto em vers玫es nightly do compilador Rust, e se tornou o padr茫o de facto dentro da comunidade Rust. O Criterion tamb茅m possui muito mais recursos em compara莽茫o com o libtest bench.

Uma alternativa experimental ao Criterion 茅 o Iai, do mesmo criador do Criterion. No entanto, ele usa contagem de instru莽玫es em vez de tempo real: instru莽玫es da CPU, acessos L1, acessos L2 e acessos 脿 RAM. Isso permite benchmarking de tiro 煤nico, uma vez que essas m茅tricas devem permanecer quase id锚nticas entre as execu莽玫es.

Todos os tr锚s s茫o suportados pelo Bencher. Ent茫o, por que escolher o libtest bench? Pode ser uma boa ideia se voc锚 est谩 tentando limitar as depend锚ncias externas do seu projeto e o seu projeto j谩 est谩 a utilizando a toolchain nightly. Fora isso, eu sugiro o uso do Criterion ou do Iai, dependendo do seu caso de uso.

Instalar o Rust nightly

Dito isso, vamos usar o libtest bench, ent茫o vamos configurar nossa toolchain do Rust para nightly. Crie um arquivo rust-toolchain.toml na raiz do seu projeto game, ao lado de Cargo.toml.

[toolchain]
channel = "nightly"

A estrutura do seu diret贸rio agora deve se parecer com isso:

game
鈹溾攢鈹 Cargo.lock
鈹溾攢鈹 Cargo.toml
鈹溾攢鈹 rust-toolchain.toml
鈹斺攢鈹 src
鈹斺攢鈹 main.rs

Ap贸s a conclus茫o, execute novamente cargo run. Deve levar um minuto para a nova toolchain nightly ser instalada antes de ser executada novamente e fornecer a mesma sa铆da de antes.

Refatora莽茫o do FizzBuzz

Para testar nossa aplica莽茫o FizzBuzz, precisamos desacoplar nossa l贸gica da fun莽茫o main do programa. Os bancos de teste n茫o conseguem testar a fun莽茫o main.

Atualize seu c贸digo para ficar assim:

fn main() {
for i in 1..=100 {
play_game(i);
}
}
pub fn play_game(n: u32) {
println!("{}", fizz_buzz(n));
}
pub fn fizz_buzz(n: u32) -> String {
match (n % 3, n % 5) {
(0, 0) => "FizzBuzz".to_string(),
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
(_, _) => n.to_string(),
}
}

Agora, separamos nosso c贸digo em tr锚s fun莽玫es diferentes:

  • main: A entrada principal do nosso programa que itera pelos n煤meros de 1 a 100 inclusivo e chama play_game para cada n煤mero.
  • play_game: Recebe um inteiro n茫o assinado n, chama fizz_buzz com esse n煤mero e imprime o resultado.
  • fizz_buzz: Recebe um inteiro n茫o assinado n e realiza a l贸gica Fizz, Buzz, FizzBuzz, ou n煤mero retornando o resultado como uma string.

Benchmarking FizzBuzz

Para usar a crate libtest inst谩vel, precisamos habilitar o recurso test para o nosso c贸digo e importar a crate test. Adicione o seguinte no topo de main.rs:

#![feature(test)]
extern crate test;

Agora estamos prontos para adicionar nosso primeiro benchmark! Adicione o seguinte no final de main.rs:

#[cfg(test)]
mod benchmarks {
use test::Bencher;
use super::play_game;
#[bench]
fn bench_play_game(b: &mut Bencher) {
b.iter(|| {
std::hint::black_box(for i in 1..=100 {
play_game(i)
});
});
}
}
  • Crie um m贸dulo chamado benchmarks e defina a configura莽茫o do compilador para o modo test.
  • Importe o executor de benchmarks Bencher. (馃惏 Que nome legal!)
  • Importe nossa fun莽茫o play_game.
  • Crie um benchmark chamado bench_play_game que recebe uma refer锚ncia mut谩vel para Bencher.
  • Defina o atributo #[bench] para indicar que bench_play_game 茅 um benchmark.
  • Use a inst芒ncia Bencher (b) para executar nosso macro-benchmark v谩rias vezes.
  • Execute nosso macro-benchmark dentro de uma 鈥渃aixa preta鈥 para que o compilador n茫o otimize nosso c贸digo.
  • Itere de 1 a 100 de forma inclusiva.
  • Para cada n煤mero, chame play_game.

Agora estamos prontos para fazer o benchmark de nosso c贸digo, execute cargo bench:

$ cargo bench
Compiling playground v0.0.1 (/home/bencher)
Finished bench [optimized] target(s) in 0.02s
Running unittests src/main.rs (target/release/deps/game-68f58c96f4025bd4)
running 1 test
test benchmarks::bench_play_game ... bench: 4,879 ns/iter (+/- 170)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in 0.68s

馃惏 Vamos agitar o p茅 de alface! Temos nossas primeiras m茅tricas de benchmark!

Finalmente, podemos descansar nossas cansadas cabe莽as de desenvolvedores鈥 Brincadeira, nossos usu谩rios querem um novo recurso!

Escreva FizzBuzzFibonacci em Rust

Nossos Indicadores Chave de Desempenho (KPIs) est茫o em baixa, ent茫o nosso Gerente de Produto (PM) quer que adicionemos um novo recurso. Ap贸s muitas discuss玫es e v谩rias entrevistas com usu谩rios, decidiu-se que o bom e velho FizzBuzz n茫o 茅 suficiente. As crian莽as de hoje querem um novo jogo, FizzBuzzFibonacci.

As regras para FizzBuzzFibonacci s茫o as seguintes:

Escreva um programa que imprime os n煤meros inteiros de 1 a 100 (inclusive):

  • Para m煤ltiplos de tr锚s, imprima Fizz
  • Para m煤ltiplos de cinco, imprima Buzz
  • Para m煤ltiplos de ambos tr锚s e cinco, imprima FizzBuzz
  • Para n煤meros que fazem parte da sequ锚ncia de Fibonacci, apenas imprima Fibonacci
  • Para todos os outros, imprima o n煤mero

A Sequ锚ncia de Fibonacci 茅 uma s茅rie na qual cada n煤mero 茅 a soma dos dois n煤meros precedentes. Por exemplo, come莽ando com 0 e 1 o pr贸ximo n煤mero na sequ锚ncia de Fibonacci seria 1. Seguido por: 2, 3, 5, 8 e assim por diante. N煤meros que fazem parte da Sequ锚ncia de Fibonacci s茫o conhecidos como n煤meros de Fibonacci. Ent茫o, teremos que escrever uma fun莽茫o que detecte n煤meros de Fibonacci.

Existem muitas maneiras de escrever a sequ锚ncia de Fibonacci e, da mesma forma, muitas maneiras de detectar um n煤mero de Fibonacci. Ent茫o, vamos com a minha favorita:

fn is_fibonacci_number(n: u32) -> bool {
for i in 0..=n {
let (mut previous, mut current) = (0, 1);
while current < i {
let next = previous + current;
previous = current;
current = next;
}
if current == n {
return true;
}
}
false
}
  • Crie uma fun莽茫o chamada is_fibonacci_number que recebe um n煤mero inteiro sem sinal e retorna um booleano.
  • Itere para todos os n煤meros de 0 ao nosso n煤mero espec铆fico n inclusive.
  • Inicialize nossa sequ锚ncia Fibonacci come莽ando com 0 e 1 como os n煤meros anterior e atual respectivamente.
  • Itere enquanto o n煤mero atual for menor que a itera莽茫o atual i.
  • Adicione o n煤mero atual e anterior para obter o n煤mero pr贸ximo.
  • Atualize o n煤mero anterior para o n煤mero atual.
  • Atualize o n煤mero atual para o n煤mero pr贸ximo.
  • Uma vez que atual for maior ou igual ao n煤mero especifico n, n贸s sairemos do loop.
  • Verifique se o n煤mero atual 茅 igual ao n煤mero especificado n e, se for, retorne true.
  • Caso contr谩rio, retorne false.

Agora precisaremos atualizar nossa fun莽茫o fizz_buzz:

pub fn fizz_buzz_fibonacci(n: u32) -> String {
if is_fibonacci_number(n) {
"Fibonacci".to_string()
} else {
match (n % 3, n % 5) {
(0, 0) => "FizzBuzz".to_string(),
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
(_, _) => n.to_string(),
}
}
}
  • Renomeie a fun莽茫o fizz_buzz para fizz_buzz_fibonacci para torn谩-la mais descritiva.
  • Chame nossa fun莽茫o auxiliar is_fibonacci_number.
  • Se o resultado de is_fibonacci_number for true retorne Fibonacci.
  • Se o resultado de is_fibonacci_number for false, execute a mesma l贸gica Fizz, Buzz, FizzBuzz, ou n煤mero retornando o resultado.

Como renomeamos fizz_buzz para fizz_buzz_fibonacci, tamb茅m precisamos atualizar nossa fun莽茫o play_game:

pub fn play_game(n: u32) {
println!("{}", fizz_buzz_fibonacci(n));
}

Ambas as fun莽玫es main e bench_play_game podem permanecer exatamente as mesmas.

Benchmarking FizzBuzzFibonacci

Agora podemos executar novamente nosso benchmark:

$ cargo bench
Compiling playground v0.0.1 (/home/bencher)
Finished bench [optimized] target(s) in 0.00s
Running unittests src/main.rs (target/release/deps/game-68f58c96f4025bd4)
running 1 test
test benchmarks::bench_play_game ... bench: 22,167 ns/iter (+/- 502)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in 0.62s

Vendo nosso hist贸rico no terminal, podemos fazer uma compara莽茫o visual entre o desempenho dos nossos jogos FizzBuzz e FizzBuzzFibonacci: 4,879 ns vs 22,167 ns. Seus n煤meros ser茫o um pouco diferentes dos meus. No entanto, a diferen莽a entre os dois jogos provavelmente est谩 na faixa de 5x. Parece bom para mim! Especialmente por adicionarmos um recurso com um nome t茫o sofisticado como Fibonacci ao nosso jogo. As crian莽as v茫o adorar!

Expandindo FizzBuzzFibonacci em Rust

Nosso jogo 茅 um sucesso! As crian莽as realmente adoram jogar FizzBuzzFibonacci. Tanto que a dire莽茫o quer uma sequ锚ncia. Mas vivemos em um mundo moderno, precisamos de Receita Anual Recorrente (ARR) e n茫o de compras 煤nicas! A nova vis茫o para o nosso jogo 茅 que ele seja aberto, sem mais viver entre os limites de 1 e 100 (mesmo que inclusivo). N茫o, estamos partindo para novas fronteiras!

As regras para o Open World FizzBuzzFibonacci s茫o as seguintes:

Escreva um programa que aceite qualquer n煤mero inteiro positivo e imprima:

  • Para m煤ltiplos de tr锚s, imprima Fizz
  • Para m煤ltiplos de cinco, imprima Buzz
  • Para m煤ltiplos de ambos tr锚s e cinco, imprima FizzBuzz
  • Para n煤meros que fazem parte da sequ锚ncia de Fibonacci, apenas imprima Fibonacci
  • Para todos os outros, imprima o n煤mero

Para que nosso jogo funcione para qualquer n煤mero, precisaremos aceitar um argumento de linha de comando. Atualize a fun莽茫o main para ficar assim:

fn main() {
let args: Vec<String> = std::env::args().collect();
let i = args
.get(1)
.map(|s| s.parse::<u32>())
.unwrap_or(Ok(15))
.unwrap_or(15);
play_game(i);
}
  • Colete todos os argumentos (args) passados para o nosso jogo a partir da linha de comando.
  • Pegue o primeiro argumento passado para o nosso jogo e analise-o como um inteiro n茫o assinado i.
  • Se a an谩lise falhar ou nenhum argumento for passado, use por padr茫o o nosso jogo com 15 como entrada.
  • Finalmente, jogue nosso jogo com o novo inteiro n茫o assinado i analisado.

Agora podemos jogar nosso jogo com qualquer n煤mero! Use cargo run seguido de -- para passar argumentos para o nosso jogo:

$ cargo run -- 9
Compiling playground v0.0.1 (/home/bencher)
Finished dev [unoptimized + debuginfo] target(s) in 0.44s
Running `target/debug/game 9`
Fizz
$ cargo run -- 10
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/game 10`
Buzz
$ cargo run -- 13
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/game 13`
Fibonacci

E se omitirmos ou fornecermos um n煤mero inv谩lido:

$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/game`
FizzBuzz
$ cargo run -- bad
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/game bad`
FizzBuzz

Nossa, que teste completo! O CI passou. Nossos chefes est茫o entusiasmados. Vamos lan莽谩-lo! 馃殌

O Fim


SpongeBob SquarePants Tr锚s Semanas Depois
Meme Est谩 Tudo Bem

馃惏 鈥 o fim da sua carreira talvez?


Brincadeira! Tudo est谩 pegando fogo! 馃敟

Bem, a princ铆pio, tudo parecia estar indo bem. Ent茫o, 脿s 02:07 da madrugada de s谩bado, meu pager disparou:

馃摕 Seu jogo est谩 pegando fogo! 馃敟

Ap贸s sair da cama 脿s pressas, tentei descobrir o que estava acontecendo. Eu tentei pesquisar nos logs, mas era dif铆cil porque tudo continuava travando. Finalmente, encontrei o problema. As crian莽as! Elas adoravam tanto nosso jogo que jogavam at茅 chegar a um milh茫o! Num lampejo de brilhantismo, adicionei dois novos benchmarks:

#[bench]
fn bench_play_game_100(b: &mut Bencher) {
b.iter(|| std::hint::black_box(play_game(100)));
}
#[bench]
fn bench_play_game_1_000_000(b: &mut Bencher) {
b.iter(|| std::hint::black_box(play_game(1_000_000)));
}
  • Um micro-benchmark bench_play_game_100 para jogar o jogo com o n煤mero cem (100)
  • Um micro-benchmark bench_play_game_1_000_000 para jogar o jogo com o n煤mero um milh茫o (1_000_000)

Quando eu executei, eu obtive isso:

$ cargo bench
Compiling game v0.1.0 (/home/bencher)
Finished bench [optimized] target(s) in 0.75s
Running unittests src/main.rs (target/release/deps/game-6e1cb3355509b761)
running 3 tests
test benchmarks::bench_play_game ... bench: 22,458 ns/iter (+/- 1,508)
test benchmarks::bench_play_game_100 ... bench: 439 ns/iter (+/- 21)

Espere por isso鈥 espere por isso鈥

test benchmarks::bench_play_game_1_000_000 ... bench: 9,586,977 ns/iter (+/- 15,923)

O qu锚! 439 ns x 1,000 deveria ser 439,000 ns e n茫o 9,586,977 ns 馃く Mesmo eu tendo acertado meu c贸digo da sequ锚ncia Fibonacci funcionalmente, devo ter algum bug de desempenho por a铆.

Corrigindo FizzBuzzFibonacci em Rust

Vamos dar outra olhada naquela fun莽茫o is_fibonacci_number:

fn is_fibonacci_number(n: u32) -> bool {
for i in 0..=n {
let (mut previous, mut current) = (0, 1);
while current < i {
let next = previous + current;
previous = current;
current = next;
}
if current == n {
return true;
}
}
false
}

Agora que estou pensando em desempenho, percebo que tenho um loop extra desnecess谩rio. Podemos nos livrar completamente do loop for i in 0..=n {} e apenas comparar o valor atual com o n煤mero dado (n) 馃う

fn is_fibonacci_number(n: u32) -> bool {
let (mut previous, mut current) = (0, 1);
while current < n {
let next = previous + current;
previous = current;
current = next;
}
current == n
}
  • Atualize sua fun莽茫o is_fibonacci_number.
  • Inicialize nossa sequ锚ncia de Fibonacci come莽ando com 0 e 1 como os n煤meros anterior e atual, respectivamente.
  • Itere enquanto o n煤mero atual for menor que o n煤mero dado n.
  • Adicione o n煤mero anterior e atual para obter o n煤mero pr贸ximo.
  • Atualize o n煤mero anterior para o n煤mero atual.
  • Atualize o n煤mero atual para o n煤mero pr贸ximo.
  • Uma vez que atual seja maior ou igual ao n煤mero dado n, sairemos do loop.
  • Verifique se o n煤mero atual 茅 igual ao n煤mero dado n e retorne esse resultado.

Agora, vamos executar novamente esses benchmarks e ver como n贸s nos sa铆mos:

$ cargo bench
Compiling game v0.1.0 (/home/bencher)
Finished bench [optimized] target(s) in 0.75s
Running unittests src/main.rs (target/release/deps/game-6e1cb3355509b761)
running 3 tests
test benchmarks::bench_play_game ... bench: 5,570 ns/iter (+/- 390)
test benchmarks::bench_play_game_100 ... bench: 46 ns/iter (+/- 3)
test benchmarks::bench_play_game_1_000_000 ... bench: 53 ns/iter (+/- 4)
test result: ok. 0 passed; 0 failed; 0 ignored; 3 measured; 0 filtered out; finished in 9.24s

Nossa! Nosso benchmark bench_play_game est谩 de volta aonde estava para o FizzBuzz original. Eu queria lembrar exatamente qual era a pontua莽茫o. J谩 se passaram tr锚s semanas. O hist贸rico do meu terminal n茫o vai t茫o longe. Mas acho que est谩 perto!

O benchmark bench_play_game_100 caiu quase 10x, de 439 ns para 46 ns. E o benchmark bench_play_game_1_000_000 caiu mais de 10,000x! De 9,586,977 ns para 53 ns!

馃惏 Pelo menos pegamos esse bug de desempenho antes de ir para produ莽茫o鈥 ah, certo. Esquece鈥

Detecte Regress玫es de Desempenho em CI

Os executivos n茫o ficaram felizes com a enxurrada de cr铆ticas negativas que nosso jogo recebeu devido ao meu pequeno bug de desempenho. Eles me disseram para n茫o deixar isso acontecer de novo, e quando perguntei como, eles simplesmente me disseram para n茫o faz锚-lo novamente. Como eu deveria gerenciar isso鈥

Felizmente, encontrei esta incr铆vel ferramenta open source chamada Bencher. Existe um n铆vel gratuito super generoso, ent茫o posso apenas usar Bencher Cloud para meus projetos pessoais. E no trabalho, onde tudo precisa estar em nossa nuvem privada, comecei a usar Bencher Auto-Hospedado.

Bencher tem adaptadores integrados, por isso 茅 f谩cil de integrar ao CI. Ap贸s seguir o guia R谩pido In铆cio, consegui executar meus benchmarks e rastre谩-los com o Bencher.

$ bencher run --project game "cargo bench"
Finished bench [optimized] target(s) in 0.03s
Running unittests src/main.rs (target/release/deps/game-6e1cb3355509b761)
running 3 tests
test benchmarks::bench_play_game ... bench: 5,690 ns/iter (+/- 1,091)
test benchmarks::bench_play_game_100 ... bench: 48 ns/iter (+/- 7)
test benchmarks::bench_play_game_1_000_000 ... bench: 51 ns/iter (+/- 3)
test result: ok. 0 passed; 0 failed; 0 ignored; 3 measured; 0 filtered out; finished in 2.81s
Bencher New Report:
...
View results:
- benchmarks::bench_play_game (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=077449e5-5b45-4c00-bdfb-3a277413180d&start_time=1697224006000&end_time=1699816009000&upper_boundary=true
- benchmarks::bench_play_game_100 (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=96508869-4fa2-44ac-8e60-b635b83a17b7&start_time=1697224006000&end_time=1699816009000&upper_boundary=true
- benchmarks::bench_play_game_1_000_000 (Latency): https://bencher.dev/console/projects/game/perf?measures=52507e04-ffd9-4021-b141-7d4b9f1e9194&branches=3a27b3ce-225c-4076-af7c-75adbc34ef9a&testbeds=bc05ed88-74c1-430d-b96a-5394fdd18bb0&benchmarks=ff014217-4570-42ea-8813-6ed0284500a4&start_time=1697224006000&end_time=1699816009000&upper_boundary=true

Usando este incr铆vel dispositivo de viagem no tempo que um simp谩tico coelho me deu, consegui voltar ao passado e reviver o que teria acontecido se estiv茅ssemos usando o Bencher desde o in铆cio. Voc锚 pode ver onde fizemos pela primeira vez o push da implementa莽茫o bugada de FizzBuzzFibonacci. Imediatamente recebi falhas no CI como um coment谩rio na minha solicita莽茫o de pull. No mesmo dia, corrigi o bug de desempenho, eliminando aquele loop extra e desnecess谩rio. Sem inc锚ndios. Apenas usu谩rios felizes.

Bencher: Benchmarking Cont铆nuo

馃惏 Bencher

Bencher 茅 um conjunto de ferramentas de benchmarking cont铆nuas. J谩 teve algum impacto de regress茫o de desempenho nos seus usu谩rios? Bencher poderia ter prevenido isso. Bencher permite que voc锚 detecte e previna regress玫es de desempenho antes que cheguem 脿 produ莽茫o.

  • Execute: Execute seus benchmarks localmente ou no CI usando suas ferramentas de benchmarking favoritas. O CLI bencher simplesmente envolve seu harness de benchmark existente e armazena seus resultados.
  • Rastreie: Acompanhe os resultados de seus benchmarks ao longo do tempo. Monitore, consulte e fa莽a gr谩ficos dos resultados usando o console web do Bencher baseado na branch de origem, testbed e medida.
  • Capture: Capture regress玫es de desempenho no CI. Bencher usa an谩lises personaliz谩veis e de 煤ltima gera莽茫o para detectar regress玫es de desempenho antes que elas cheguem 脿 produ莽茫o.

Pelos mesmos motivos que os testes de unidade s茫o executados no CI para prevenir regress玫es de funcionalidades, benchmarks deveriam ser executados no CI com o Bencher para prevenir regress玫es de desempenho. Bugs de desempenho s茫o bugs!

Comece a capturar regress玫es de desempenho no CI 鈥 experimente o Bencher Cloud gratuitamente.

馃 Este documento foi gerado automaticamente pelo OpenAI GPT-4. Pode n茫o ser preciso e pode conter erros. Se voc锚 encontrar algum erro, abra um problema no GitHub.