C贸mo hacer pruebas de rendimiento en Rust con el banco de pruebas libtest

Everett Pompeii

Everett Pompeii


驴Qu茅 es la Evaluaci贸n Comparativa?

La evaluaci贸n comparativa es la pr谩ctica de probar el rendimiento de tu c贸digo para ver qu茅 tan r谩pido (latencia) o cu谩nto (rendimiento) trabajo puede hacer. Este paso, a menudo pasado por alto en el desarrollo de software, es crucial para crear y mantener un c贸digo r谩pido y de alto rendimiento. La evaluaci贸n comparativa proporciona las m茅tricas necesarias para que los desarrolladores comprendan c贸mo se comporta su c贸digo bajo diversas cargas de trabajo y condiciones. Por las mismas razones por las cuales escribes pruebas unitarias y de integraci贸n para prevenir regresiones de caracter铆sticas, debes escribir evaluaciones comparativas para prevenir regresiones de rendimiento. 隆Los errores de rendimiento son errores!

Escribe FizzBuzz en Rust

Para escribir evaluaciones comparativas, necesitamos alg煤n c贸digo fuente para comparar. Para empezar, vamos a escribir un programa muy simple, FizzBuzz.

Las reglas para FizzBuzz son las siguientes:

Escribe un programa que imprima los n煤meros enteros del 1 al 100 (incluyendo ambos):

  • Para m煤ltiplos de tres, imprime Fizz
  • Para m煤ltiplos de cinco, imprime Buzz
  • Para m煤ltiplos de ambos, tres y cinco, imprime FizzBuzz
  • Para todos los dem谩s, imprime el n煤mero

Hay muchas formas de escribir FizzBuzz. As铆 que vamos a elegir mi favorita:

fn main() {
for i in 1..=100 {
match (i % 3, i % 5) {
(0, 0) => println!("FizzBuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
(_, _) => println!("{i}"),
}
}
}
  • Crea una funci贸n main
  • Itera desde 1 hasta 100 de manera inclusiva.
  • Para cada n煤mero, calcula el m贸dulo (resto despu茅s de la divisi贸n) tanto para 3 como para 5.
  • Coincide el patr贸n con los dos restos. Si el resto es 0, entonces el n煤mero es m煤ltiplo del factor dado.
  • Si el resto es 0 tanto para 3 como para 5, imprime FizzBuzz.
  • Si el resto es 0 solo para 3, entonces imprime Fizz.
  • Si el resto es 0 solo para 5, entonces imprime Buzz.
  • De lo contrario, simplemente imprime el n煤mero.

Sigue Paso a Paso

Para seguir este tutorial paso a paso, necesitar谩s instalar Rust.

馃惏 El c贸digo fuente de esta publicaci贸n est谩 disponible en GitHub

Con Rust instalado, puedes abrir una ventana de terminal e introducir: cargo init game

Luego navega hacia el directorio game reci茅n creado.

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

Deber铆as ver un directorio llamado src con un archivo llamado main.rs:

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

Reemplaza sus contenidos con la implementaci贸n de FizzBuzz de arriba. Luego ejecuta cargo run. La salida deber铆a verse as铆:

$ 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! 隆Est谩s rompiendo la entrevista de codificaci贸n!

Se deber铆a haber generado un nuevo archivo Cargo.lock:

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

Antes de avanzar m谩s, es importante discutir las diferencias entre micro-benchmarking y macro-benchmarking.

Micro-Benchmarking vs Macro-Benchmarking

Existen dos categor铆as principales de benchmarks de software: micro-benchmarks y macro-benchmarks. Los micro-benchmarks operan a un nivel similar a las pruebas unitarias. Por ejemplo, un benchmark para una funci贸n que determina Fizz, Buzz, o FizzBuzz para un 煤nico n煤mero ser铆a un micro-benchmark. Los macro-benchmarks operan a un nivel similar a las pruebas de integraci贸n. Por ejemplo, un benchmark para una funci贸n que juega el juego completo de FizzBuzz, desde 1 hasta 100, ser铆a un macro-benchmark.

Generalmente, es mejor probar al nivel m谩s bajo de abstracci贸n posible. En el caso de los benchmarks, esto los hace m谩s f谩ciles de mantener, y ayuda a reducir la cantidad de ruido en las mediciones. Sin embargo, al igual que tener algunas pruebas de extremo a extremo puede ser muy 煤til para verificar la cordura todo el sistema se junta como se esperaba, tener macro-benchmarks puede ser muy 煤til para asegurarse de que los caminos cr铆ticos a trav茅s de su software se mantienen con buen rendimiento.

Evaluaci贸n de rendimiento en Rust

Las tres opciones populares para la evaluaci贸n de rendimiento en Rust son: libtest bench, Criterion, e Iai.

libtest es el marco de evaluaci贸n de pruebas unitarias y rendimiento integrado de Rust. Aunque forma parte de la biblioteca est谩ndar de Rust, libtest bench todav铆a se considera inestable, por lo que solo est谩 disponible en versiones del compilador nightly. Para trabajar en el compilador estable de Rust, se necesita un arn茅s de evaluaci贸n de rendimiento separado. Sin embargo, ninguno est谩 siendo desarrollado activamente.

El arn茅s de evaluaci贸n de rendimiento m谩s popular dentro del ecosistema de Rust es Criterion. Funciona tanto en versiones del compilador Rust estable como nightly, y se ha convertido en el est谩ndar de facto dentro de la comunidad Rust. Criterion tambi茅n es mucho m谩s rico en funcionalidades en comparaci贸n con libtest bench.

Una alternativa experimental a Criterion es Iai, del mismo creador de Criterion. Sin embargo, utiliza el conteo de instrucciones en lugar del tiempo de pared: instrucciones de CPU, accesos a L1, accesos a L2 y accesos a RAM. Esto permite evaluaciones de rendimiento de una sola ejecuci贸n, ya que estas m茅tricas deber铆an mantenerse casi id茅nticas entre ejecuciones.

Los tres son soportados por Bencher. Entonces, 驴por qu茅 elegir banco de pruebas libtest? Puede ser una buena idea si est谩s tratando de limitar las dependencias externas de tu proyecto y tu proyecto ya est谩 usando la cadena de herramientas nightly. Fuera de eso, sugerir铆a usar Criterion o Iai dependiendo de tu caso de uso.

Instalar Rust nightly

Dicho esto, vamos a usar el banco de pruebas libtest, as铆 que vamos a poner nuestra cadena de herramientas de Rust en nightly. Crea un archivo rust-toolchain.toml en la ra铆z de tu proyecto game, junto a Cargo.toml.

[toolchain]
channel = "nightly"

Tu estructura de directorios ahora deber铆a lucir as铆:

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

Una vez que est茅 completo, vuelve a ejecutar cargo run. Deber铆a llevar un minuto para que la nueva cadena de herramientas nightly se instale antes de volver a ejecutar y darte la misma salida que antes.

Refactorizar FizzBuzz

Para probar nuestra aplicaci贸n FizzBuzz, necesitamos desacoplar nuestra l贸gica de la funci贸n main del programa. Las superestructuras de rendimiento no pueden medir el rendimiento de la funci贸n main.

Actualiza tu c贸digo para que se vea as铆:

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(),
}
}

Ahora hemos separado nuestro c贸digo en tres funciones diferentes:

  • main: El punto de entrada principal a nuestro programa que itera a trav茅s de los n煤meros del 1 al 100, inclusive, y llama a play_game para cada n煤mero.
  • play_game: Toma un n煤mero entero sin signo n, llama a fizz_buzz con ese n煤mero, e imprime el resultado.
  • fizz_buzz: Toma un n煤mero entero sin signo n y realiza la l贸gica de Fizz, Buzz, FizzBuzz o n煤mero, devolviendo el resultado como una cadena.

Haciendo benchmark de FizzBuzz

Para utilizar el inestable crate libtest necesitamos habilitar la feature test para nuestro c贸digo e importar el crate test. A帽ade lo siguiente en la parte superior de main.rs:

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

隆Ahora estamos listos para a帽adir nuestro primer benchmark! A帽ade lo siguiente en la parte inferior 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)
});
});
}
}
  • Crea un m贸dulo llamado benchmarks y establece la configuraci贸n del compilador a modo test.
  • Importa el runner de benchmark Bencher. (馃惏 隆Hey, qu茅 nombre tan cool!)
  • Importa nuestra funci贸n play_game.
  • Crea un benchmark llamado bench_play_game que recibe una referencia mutable a Bencher.
  • Establece el atributo #[bench] para indicar que bench_play_game es un benchmark.
  • Usa la instancia Bencher (b) para ejecutar nuestro macro-benchmark varias veces.
  • Ejecuta nuestro macro-benchmark dentro de una 鈥渃aja negra鈥 para que el compilador no optimice nuestro c贸digo.
  • Itera desde 1 hasta 100 inclusive.
  • Para cada n煤mero, llama a play_game.

Ahora estamos listos para hacer un benchmark de nuestro c贸digo, ejecuta 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

馃惏 隆Encendamos el ritmo! 隆Tenemos nuestras primeras m茅tricas de benchmark!

Finalmente, podemos descansar nuestras cansadas cabezas de desarrolladores鈥 S贸lo bromeaba, 隆nuestros usuarios quieren una nueva funci贸n!

Escribe FizzBuzzFibonacci en Rust

Nuestros Indicadores Clave de Desempe帽o (KPIs) est谩n bajos, por lo que nuestro Gerente de Producto (PM) quiere que agreguemos una nueva funci贸n. Despu茅s de mucho lluvia de ideas y muchas entrevistas con usuarios, se decidi贸 que el FizzBuzz de siempre no es suficiente. Los ni帽os de hoy en d铆a quieren un nuevo juego, FizzBuzzFibonacci.

Las reglas para FizzBuzzFibonacci son las siguientes:

Escribe un programa que imprima los enteros del 1 al 100 (inclusive):

  • Para m煤ltiplos de tres, imprime Fizz
  • Para m煤ltiplos de cinco, imprime Buzz
  • Para m煤ltiplos de tres y cinco, imprime FizzBuzz
  • Para n煤meros que sean parte de la secuencia de Fibonacci, solo imprime Fibonacci
  • Para todos los dem谩s, imprime el n煤mero

La secuencia de Fibonacci es una serie en la que cada n煤mero es la suma de los dos n煤meros anteriores. Por ejemplo, comenzando con 0 y 1 el siguiente n煤mero en la secuencia de Fibonacci ser铆a 1. Seguido de: 2, 3, 5, 8 y as铆 sucesivamente. Los n煤meros que forman parte de la secuencia de Fibonacci se conocen como n煤meros de Fibonacci. Por lo tanto, tendremos que escribir una funci贸n que detecte los n煤meros de Fibonacci.

Hay muchas formas de escribir la secuencia de Fibonacci y de igual forma muchas maneras de detectar un n煤mero de Fibonacci. As铆 que elegiremos mi forma 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
}
  • Crea una funci贸n llamada is_fibonacci_number que toma un n煤mero entero sin signo y devuelve un booleano.
  • Itera para todos los n煤meros desde 0 hasta nuestro n煤mero dado n inclusive.
  • Inicializa nuestra secuencia Fibonacci comenzando con 0 y 1 como los n煤meros anterior y actual respectivamente.
  • Itera mientras el n煤mero actual sea menor que la iteraci贸n actual i.
  • Suma el n煤mero anterior y el n煤mero actual para obtener el n煤mero siguiente.
  • Actualiza el n煤mero anterior al n煤mero actual.
  • Actualiza el n煤mero actual al n煤mero siguiente.
  • Una vez que actual sea mayor o igual al n煤mero dado n, saldremos del bucle.
  • Verifica si el n煤mero actual es igual al n煤mero dado n y si es as铆, devuelve true.
  • De lo contrario, devuelve false.

Ahora necesitaremos actualizar nuestra funci贸n 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(),
}
}
}
  • Renombra la funci贸n fizz_buzz a fizz_buzz_fibonacci para que sea m谩s descriptiva.
  • Llama a nuestra funci贸n auxiliar is_fibonacci_number.
  • Si el resultado de is_fibonacci_number es true, entonces devuelve Fibonacci.
  • Si el resultado de is_fibonacci_number es false, entonces realiza la misma l贸gica de Fizz, Buzz, FizzBuzz o n煤mero devolviendo el resultado.

Debido a que renombramos fizz_buzz a fizz_buzz_fibonacci tambi茅n necesitamos actualizar nuestra funci贸n play_game:

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

Tanto nuestras funciones main como bench_play_game pueden permanecer exactamente iguales.

Haciendo Benchmark de FizzBuzzFibonacci

Ahora podemos volver a ejecutar nuestro 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

Desplaz谩ndonos hacia atr谩s a trav茅s de nuestro historial de terminal, podemos hacer una comparaci贸n visual entre el rendimiento de nuestros juegos FizzBuzz y FizzBuzzFibonacci: 4,879 ns vs 22,167 ns. Tus n煤meros ser谩n un poco diferentes a los m铆os. Sin embargo, la diferencia entre los dos juegos probablemente sea de alrededor de 5 veces. 隆Eso me parece bien! Especialmente por agregar una funci贸n tan sofisticada como Fibonacci a nuestro juego. 隆A los ni帽os les encantar谩!

Expande FizzBuzzFibonacci en Rust

隆Nuestro juego es todo un 茅xito! A los ni帽os definitivamente les encanta jugar FizzBuzzFibonacci. Tanto as铆 que lleg贸 la noticia de los ejecutivos de que quieren una secuela. Pero este es el mundo moderno, 隆necesitamos ingresos recurrentes anuales (ARR) no compras 煤nicas! La nueva visi贸n para nuestro juego es que sea abierto, 隆no m谩s limitaciones entre el 1 y el 100 (aunque sea inclusivo)! 隆No, vamos hacia nuevas fronteras!

Las reglas para Open World FizzBuzzFibonacci son las siguientes:

Escribe un programa que tome cualquier n煤mero entero positivo e imprima:

  • Para m煤ltiplos de tres, imprime Fizz
  • Para m煤ltiplos de cinco, imprime Buzz
  • Para m煤ltiplos de tres y cinco, imprime FizzBuzz
  • Para n煤meros que son parte de la secuencia Fibonacci, s贸lo imprime Fibonacci
  • Para todos los dem谩s, imprime el n煤mero

Para que nuestro juego funcione con cualquier n煤mero, necesitaremos aceptar un argumento de l铆nea de comandos. Actualiza la funci贸n main para que se vea as铆:

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);
}
  • Recolecta todos los argumentos (args) pasados a nuestro juego desde la l铆nea de comandos.
  • Obt茅n el primer argumento pasando a nuestro juego y anal铆zalo como un entero sin signo i.
  • Si el an谩lisis falla o no se pasa ning煤n argumento, por defecto, nuestro juego tomar谩 el 15 como entrada.
  • Finalmente, juega nuestro juego con el nuevo entero sin signo i.

隆Ahora podemos jugar nuestro juego con cualquier n煤mero! Usa cargo run seguido de -- para pasar argumentos a nuestro juego:

$ 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

Y si omitimos o proporcionamos un 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

Vaya, 隆eso fue una prueba exhaustiva! CI pasa. Nuestros jefes est谩n emocionados. 隆Vamos a lanzarlo! 馃殌

El Fin


SpongeBob SquarePants Tres Semanas Despu茅s
Meme de Esto est谩 Bien

馃惏 鈥 驴el fin de tu carrera tal vez?


隆Solo bromeaba! 隆Todo est谩 en llamas! 馃敟

Bueno, al principio todo parec铆a ir bien. Y luego a las 02:07 AM del s谩bado, mi buscapersonas son贸:

馃摕 隆Tu juego est谩 en llamas! 馃敟

Despu茅s de salir de la cama a la carrera, trat茅 de averiguar qu茅 estaba pasando. Intent茅 buscar en los registros, pero eso fue dif铆cil porque todo segu铆a fallando. Finalmente, encontr茅 el problema. 隆Los ni帽os! Les encantaba tanto nuestro juego, que lo estaban jugando hasta llegar al mill贸n! En un destello de genialidad, agregu茅 dos nuevos 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)));
}
  • Un micro-benchmark bench_play_game_100 para jugar el juego con el n煤mero cien (100)
  • Un micro-benchmark bench_play_game_1_000_000 para jugar el juego con el n煤mero un mill贸n (1_000_000)

Cuando lo ejecut茅, obtuve esto:

$ 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)

Esp茅ralo鈥 esp茅ralo鈥

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

隆Qu茅! 439 ns x 1,000 deber铆a ser 439,000 ns no 9,586,977 ns 馃く Aunque obtuve mi funci贸n de c贸digo de secuencia de Fibonacci funcionalmente correcta, debo tener un error de rendimiento en alg煤n lugar.

Correcci贸n de FizzBuzzFibonacci en Rust

Volviendo a mirar la funci贸n 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
}

Ahora que estoy pensando en el rendimiento, me doy cuenta de que tengo un ciclo extra innecesario. Podemos deshacernos por completo del bucle for i in 0..=n {} y simplemente comparar el valor current con el 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
}
  • Actualiza tu funci贸n de is_fibonacci_number.
  • Inicializa nuestra secuencia de Fibonacci comenzando con el 0 y 1 como los n煤meros previous y current respectivamente.
  • Itera mientras que el n煤mero current sea menor al n煤mero dado n.
  • Suma el n煤mero previous y current para obtener el n煤mero next.
  • Actualiza el n煤mero previous al n煤mero current.
  • Actualiza el n煤mero current al n煤mero next.
  • Una vez que current es mayor o igual al n煤mero dado n, saldremos del bucle.
  • Comprobar si el n煤mero current es igual al n煤mero dado n y devolver ese resultado.

Ahora vamos a volver a ejecutar esas pruebas y ver c贸mo nos fue:

$ 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

隆Oh, vaya! Nuestro benchmark bench_play_game ha vuelto a estar cerca de donde estaba para el original FizzBuzz. Ojal谩 pudiera recordar exactamente cu谩l era esa puntuaci贸n. Han pasado tres semanas. Mi historial de terminal no llega tan lejos. 隆Pero creo que est谩 cerca!

El benchmark bench_play_game_100 ha bajado casi 10 veces, de 439 ns a 46 ns. 隆Y el benchmark bench_play_game_1_000_000 ha bajado m谩s de 10,000 veces! 隆De 9,586,977 ns a 53 ns!

馃惏 Hey, al menos detectamos este error de rendimiento antes de que llegara a producci贸n鈥 oh, cierto. No importa鈥

Detectar Retrocesos de Rendimiento en CI

Los ejecutivos no estaban contentos con la avalancha de cr铆ticas negativas que recibi贸 nuestro juego debido a mi peque帽o error de rendimiento. Me dijeron que no dejara que volviera a ocurrir, y cuando les pregunt茅 c贸mo, simplemente me dijeron que no volviera a hacerlo. 隆驴C贸mo se supone que deber铆a manejar eso鈥

Afortunadamente, he encontrado esta incre铆ble herramienta de c贸digo abierto llamada Bencher. Hay un nivel gratuito s煤per generoso, as铆 que puedo usar Bencher Cloud para mis proyectos personales. Y en el trabajo, donde todo debe estar en nuestra nube privada, he comenzado a usar Bencher Self-Hosted.

Bencher tiene adaptadores incorporados, por lo que es f谩cil de integrar en CI. Despu茅s de seguir la gu铆a de inicio r谩pido, ya puedo ejecutar mis referencias y seguir su progreso con 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 pr谩ctico dispositivo de viaje en el tiempo que un amable conejo me dio, pude retroceder en el tiempo y reproducir lo que habr铆a sucedido si hubi茅ramos estado utilizando Bencher todo el tiempo. Puedes ver d贸nde publicamos por primera vez la implementaci贸n defectuosa de FizzBuzzFibonacci. Inmediatamente recib铆 fallas en CI como un comentario en mi solicitud de extracci贸n. Ese mismo d铆a, arregl茅 el error de rendimiento, eliminando ese bucle extra innecesario. No hubo incendios. Solo usuarios contentos.

Bencher: Benchmarking continuo

馃惏 Bencher

Bencher es un conjunto de herramientas de benchmarking continuo. 驴Alguna vez has tenido un impacto de regresi贸n de rendimiento en tus usuarios? Bencher podr铆a haber evitado que eso sucediera. Bencher te permite detectar y prevenir las regresiones de rendimiento antes de que lleguen a producci贸n.

  • Ejecutar: Ejecute sus benchmarks localmente o en CI usando sus herramientas de benchmarking favoritas. La CLI bencher simplemente envuelve su arn茅s de benchmarks existente y almacena sus resultados.
  • Seguir: Sigue los resultados de tus benchmarks con el tiempo. Monitoriza, realiza consultas y representa gr谩ficamente los resultados utilizando la consola web de Bencher bas谩ndose en la rama de origen, el banco de pruebas y la medida.
  • Capturar: Captura las regresiones de rendimiento en CI. Bencher utiliza anal铆ticas de vanguardia y personalizables para detectar regresiones de rendimiento antes de que lleguen a producci贸n.

Por las mismas razones que las pruebas unitarias se ejecutan en CI para prevenir regresiones funcionales, los benchmarks deber铆an ejecutarse en CI con Bencher para prevenir regresiones de rendimiento. 隆Los errores de rendimiento son errores!

Empiece a capturar regresiones de rendimiento en CI 鈥 prueba Bencher Cloud gratis.

馃 Este documento fue generado autom谩ticamente por OpenAI GPT-4. Puede que no sea exacto y contenga errores. Si encuentra alg煤n error, abra un problema en GitHub.