Comment mettre en benchmark le code Rust avec le banc d'essai libtest

Everett Pompeii

Everett Pompeii


Qu’est-ce que le Benchmarking ?

Le benchmarking est la pratique consistant Ă  tester les performances de votre code pour voir Ă  quelle vitesse (latence) ou combien (dĂ©bit) de travail il peut effectuer. Cette Ă©tape souvent nĂ©gligĂ©e dans le dĂ©veloppement logiciel est cruciale pour crĂ©er et maintenir un code rapide et performant. Le benchmarking fournit les mĂ©triques nĂ©cessaires aux dĂ©veloppeurs pour comprendre comment leur code se comporte sous diverses charges de travail et conditions. Pour les mĂȘmes raisons que vous Ă©crivez des tests unitaires et d’intĂ©gration pour Ă©viter les rĂ©gressions de fonctionnalitĂ©s, vous devriez Ă©crire des benchmarks pour Ă©viter les rĂ©gressions de performances. Les bugs de performance sont des bugs !

Écrire FizzBuzz en Rust

Pour Ă©crire des benchmarks, nous avons besoin de code source Ă  Ă©valuer. Pour commencer, nous allons Ă©crire un programme trĂšs simple, FizzBuzz.

Les rĂšgles pour FizzBuzz sont les suivantes :

Écrivez un programme qui imprime les entiers de 1 à 100 (inclus) :

  • Pour les multiples de trois, imprimez Fizz
  • Pour les multiples de cinq, imprimez Buzz
  • Pour les multiples de trois et de cinq, imprimez FizzBuzz
  • Pour tous les autres, imprimez le numĂ©ro

Il existe de nombreuses façons d’écrire FizzBuzz. Nous allons donc choisir ma prĂ©fĂ©rĂ©e :

fn main() {
for i in 1..=100 {
match (i % 3, i % 5) {
(0, 0) => println!("FizzBuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
(_, _) => println!("{i}"),
}
}
}
  • CrĂ©er une fonction main
  • ItĂ©rer de 1 Ă  100 inclusivement.
  • Pour chaque nombre, calculez le modulo (reste aprĂšs division) pour 3 et 5.
  • Utilisez le pattern matching sur les deux restes. Si le reste est 0, alors le nombre est un multiple du facteur donnĂ©.
  • Si le reste est 0 pour 3 et 5, alors imprimez FizzBuzz.
  • Si le reste est 0 pour seulement 3, alors imprimez Fizz.
  • Si le reste est 0 pour seulement 5, alors imprimez Buzz.
  • Sinon, imprimez simplement le nombre.

Suivre Ă©tape par Ă©tape

Pour suivre ce tutoriel Ă©tape par Ă©tape, vous devrez installer Rust.

🐰 Le code source de ce post est disponible sur GitHub

Avec Rust installĂ©, vous pouvez alors ouvrir une fenĂȘtre de terminal et entrer : cargo init game

Ensuite, naviguez dans le nouveau répertoire game créé.

game
├── Cargo.toml
└── src
└── main.rs

Vous devriez voir un répertoire appelé src avec un fichier nommé main.rs :

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

Remplacez son contenu par l’implĂ©mentation FizzBuzz ci-dessus. Puis exĂ©cutez cargo run. Le rĂ©sultat devrait ressembler Ă  :

$ 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

🐰 Bam ! Vous craquez l’entretien de codage !

Un nouveau fichier Cargo.lock devrait avoir été généré :

game
├── Cargo.lock
├── Cargo.toml
└── src
└── main.rs

Avant de continuer, il est important de discuter des différences entre le micro-benchmarking et le macro-benchmarking.

Micro-Benchmarking vs Macro-Benchmarking

Il existe deux grandes catĂ©gories de benchmarks logiciels : les micro-benchmarks et les macro-benchmarks. Les micro-benchmarks fonctionnent Ă  un niveau similaire aux tests unitaires. Par exemple, un benchmark pour une fonction qui dĂ©termine Fizz, Buzz, ou FizzBuzz pour un seul nombre serait un micro-benchmark. Les macro-benchmarks fonctionnent Ă  un niveau similaire aux tests d’intĂ©gration. Par exemple, un benchmark pour une fonction qui joue l’ensemble du jeu de FizzBuzz, de 1 Ă  100, serait un macro-benchmark.

GĂ©nĂ©ralement, il est prĂ©fĂ©rable de tester au niveau le plus bas d’abstraction possible. Dans le cas des benchmarks, cela les rend Ă  la fois plus faciles Ă  maintenir, et cela aide Ă  rĂ©duire le bruit dans les mesures. Cependant, tout comme avoir des tests de bout en bout peut ĂȘtre trĂšs utile pour vĂ©rifier la cohĂ©rence de l’ensemble du systĂšme tel que prĂ©vu, avoir des macro-benchmarks peut ĂȘtre trĂšs utile pour s’assurer que les chemins critiques Ă  travers votre logiciel restent performants.

Benchmarking en Rust

Les trois options populaires pour le benchmarking en Rust sont : libtest bench, Criterion, et Iai.

libtest est le framework intĂ©grĂ© de tests unitaires et de benchmarking de Rust. Bien qu’il fasse partie de la bibliothĂšque standard de Rust, libtest bench est encore considĂ©rĂ© comme instable, il n’est donc disponible que sur les versions nightly du compilateur. Pour fonctionner sur le compilateur Rust stable, un harnais de benchmarking sĂ©parĂ© doit ĂȘtre utilisĂ©. Cependant, aucun des deux n’est activement dĂ©veloppĂ©.

Le harnais de benchmarking le plus populaire dans l’écosystĂšme Rust est Criterion. Il fonctionne Ă  la fois sur les versions stables et nightly du compilateur Rust, et il est devenu le standard de facto au sein de la communautĂ© Rust. Criterion est Ă©galement beaucoup plus riche en fonctionnalitĂ©s comparĂ© Ă  libtest bench.

Une alternative expĂ©rimentale Ă  Criterion est Iai, crĂ©Ă© par le mĂȘme auteur que Criterion. Cependant, il utilise des comptes d’instructions plutĂŽt que du temps d’horloge murale : instructions CPU, accĂšs L1, accĂšs L2 et accĂšs RAM. Cela permet un benchmarking en une seule passe puisque ces mĂ©triques devraient rester quasiment identiques entre les exĂ©cutions.

Tous trois sont supportĂ©s par Bencher. Alors pourquoi choisir le banc d’essai libtest ? Cela peut ĂȘtre une bonne idĂ©e si vous essayez de limiter les dĂ©pendances externes de votre projet et que votre projet utilise dĂ©jĂ  la chaĂźne d’outils nightly. En dehors de cela, je suggĂ©rerais d’utiliser soit Criterion soit Iai selon votre cas d’utilisation.

Installer Rust nightly

Cela dit, nous allons utiliser le banc d’essai libtest, alors rĂ©glons notre chaĂźne d’outils Rust sur nightly. CrĂ©ez un fichier rust-toolchain.toml Ă  la racine de votre projet game, Ă  cĂŽtĂ© de Cargo.toml.

[toolchain]
channel = "nightly"

La structure de votre répertoire devrait maintenant ressembler à ceci :

game
├── Cargo.lock
├── Cargo.toml
├── rust-toolchain.toml
└── src
└── main.rs

Une fois cela fini, relancez cargo run. Il faudra une minute pour que la nouvelle chaĂźne d’outils nightly s’installe avant de redĂ©marrer et de vous donner la mĂȘme sortie qu’auparavant.

Refactorisation FizzBuzz

Pour tester notre application FizzBuzz, nous devons découpler notre logique de la fonction principale main du programme. Les harnais de test ne peuvent pas évaluer la fonction main.

Mettez à jour votre code pour qu’il ressemble à ceci :

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

Nous avons maintenant séparé notre code en trois fonctions différentes :

  • main : L’entrĂ©e principale dans notre programme qui parcourt les nombres de 1 Ă  100 inclus et appelle play_game pour chaque nombre.
  • play_game : Prend un entier non signĂ© n, appelle fizz_buzz avec ce nombre et imprime le rĂ©sultat.
  • fizz_buzz : Prend un entier non signĂ© n et effectue la logique rĂ©elle Fizz, Buzz, FizzBuzz, ou le nombre renvoie le rĂ©sultat sous forme de chaĂźne.

Benchmarking de FizzBuzz

Pour utiliser la crate libtest instable, nous devons activer la fonction test pour notre code et importer la crate test. Ajoutez ce qui suit au tout début de main.rs :

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

Maintenant nous sommes prĂȘts Ă  ajouter notre premier benchmark ! Ajoutez ce qui suit au tout bas 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)
});
});
}
}
  • CrĂ©ez un module appelĂ© benchmarks et rĂ©glez la configuration du compilateur sur le mode test.
  • Importez le runner de benchmark Bencher. (🐰 HĂ©, cool nom !)
  • Importez notre fonction play_game.
  • CrĂ©ez un benchmark appelĂ© bench_play_game qui prend en entrĂ©e une rĂ©fĂ©rence mutable Ă  Bencher.
  • Configurez l’attribut #[bench] pour indiquer que bench_play_game est un benchmark.
  • Utilisez l’instance de Bencher (b) pour exĂ©cuter plusieurs fois notre macro-benchmark.
  • ExĂ©cutez notre macro-benchmark Ă  l’intĂ©rieur d’une “boĂźte noire” pour que le compilateur n’optimise pas notre code.
  • ItĂ©rez de 1 Ă  100 inclusivement.
  • Pour chaque nombre, appelez play_game.

Maintenant nous sommes prĂȘts pour benchmark notre code, exĂ©cutez 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

🐰 Laissons la betterave monter ! Nous avons nos premiùres mesures de benchmark !

Finalement, nous pouvons reposer nos tĂȘtes de dĂ©veloppeurs fatiguĂ©s
 Juste une blague, nos utilisateurs veulent une nouvelle fonctionnalitĂ© !

Écrire FizzBuzzFibonacci en Rust

Nos indicateurs de performance clĂ©s (KPIs) sont en baisse, donc notre chef de produit (PM) veut que nous ajoutions une nouvelle fonctionnalitĂ©. AprĂšs beaucoup de brainstorming et de nombreuses interviews d’utilisateurs, il est dĂ©cidĂ© que le bon vieux FizzBuzz ne suffit pas. Les gens d’aujourd’hui veulent un nouveau jeu, FizzBuzzFibonacci.

Les rĂšgles du FizzBuzzFibonacci sont les suivantes :

Écrivez un programme qui imprime les entiers de 1 à 100 (inclus) :

  • Pour les multiples de trois, imprimez Fizz
  • Pour les multiples de cinq, imprimez Buzz
  • Pour les multiples de trois et cinq, imprimez FizzBuzz
  • Pour les nombres qui font partie de la sĂ©quence de Fibonacci, imprimez uniquement Fibonacci
  • Pour tous les autres, imprimez le nombre

La séquence de Fibonacci est une séquence dans laquelle chaque nombre est la somme des deux précédents. Par exemple, en commençant par 0 et 1, le prochain nombre dans la séquence de Fibonacci serait 1. Suivi par : 2, 3, 5, 8 et ainsi de suite. Les nombres qui font partie de la séquence de Fibonacci sont connus sous le nom de nombres de Fibonacci. Nous allons donc devoir écrire une fonction qui détecte les nombres de Fibonacci.

Il y a plusieurs façons de générer la séquence de Fibonacci et plusieurs façons de détecter un nombre de Fibonacci. Nous allons donc choisir ma méthode préférée :

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
}
  • CrĂ©ez une fonction nommĂ©e is_fibonacci_number qui prend un entier non signĂ© et retourne un boolĂ©en.
  • ItĂ©rer pour tous les nombres de 0 Ă  notre nombre donnĂ© n inclus.
  • Initialiser notre sĂ©quence Fibonacci en commençant par 0 et 1 comme les nombres previous et current respectivement.
  • ItĂ©rer pendant que le nombre current est infĂ©rieur au nombre d’itĂ©ration i en cours.
  • Ajoutez le nombre previous et le nombre current pour obtenir le nombre next.
  • Mettre Ă  jour le nombre previous avec le nombre current.
  • Mettre Ă  jour le nombre current avec le nombre next.
  • Une fois que current est supĂ©rieur ou Ă©gal au nombre donnĂ© n, nous sortirons de la boucle.
  • VĂ©rifiez si le nombre current est Ă©gal au nombre donnĂ© n et si c’est le cas retourner true.
  • Sinon, retournez false.

Maintenant, nous devrons mettre Ă  jour notre fonction 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(),
}
}
}
  • Renommez la fonction fizz_buzz en fizz_buzz_fibonacci pour la rendre plus descriptive.
  • Appelez notre fonction associĂ©e is_fibonacci_number.
  • Si le rĂ©sultat de is_fibonacci_number est true, alors retournez Fibonacci.
  • Si le rĂ©sultat de is_fibonacci_number est false, alors effectuez la mĂȘme logique Fizz,Buzz, FizzBuzz, ou nombre en retournant le rĂ©sultat.

Parce que nous renommons fizz_buzz en fizz_buzz_fibonacci, il nous faut Ă©galement mettre Ă  jour notre fonction play_game:

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

Les fonctions main et bench_play_game peuvent rester exactement les mĂȘmes.

Benchmarking de FizzBuzzFibonacci

Maintenant nous pouvons relancer notre 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

En remontant notre historique de terminal, nous pouvons faire une comparaison visuelle entre les performances de nos jeux FizzBuzz et FizzBuzzFibonacci : 4,879 ns contre 22,167 ns. Vos chiffres seront un peu diffĂ©rents des miens. Cependant, la diffĂ©rence entre les deux jeux est probablement dans l’ordre de 5x. Cela me semble bon ! Surtout pour ajouter une fonctionnalitĂ© aussi fantaisiste que Fibonacci Ă  notre jeu. Les enfants vont adorer !

Étendre FizzBuzzFibonacci en Rust

Notre jeu est un succĂšs! Les enfants adorent jouer Ă  FizzBuzzFibonacci. Tellement que les dirigeants veulent une suite. Mais c’est le monde moderne, nous avons besoin de revenus rĂ©currents annuels (ARR) et non de ventes uniques! La nouvelle vision de notre jeu est qu’il est sans fin, plus besoin de vivre entre les limites de 1 et 100 (mĂȘme si c’est inclusif). Non, nous partons vers de nouveaux horizons!

Les rĂšgles pour Open World FizzBuzzFibonacci sont les suivantes :

Écrivez un programme qui prend en entrĂ©e n’importe quel nombre entier positif et affiche :

  • Pour les multiples de trois, affichez Fizz
  • Pour les multiples de cinq, affichez Buzz
  • Pour les multiples Ă  la fois de trois et de cinq, affichez FizzBuzz
  • Pour les nombres qui font partie de la sĂ©quence de Fibonacci, affichez uniquement Fibonacci
  • Pour tous les autres, affichez le nombre

Pour faire fonctionner notre jeu pour n’importe quel nombre, nous devrons accepter un argument de ligne de commande. Mettez à jour la fonction main pour qu’elle ressemble à ceci :

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);
}
  • Collectez tous les arguments (args) passĂ©s Ă  notre jeu depuis la ligne de commande.
  • Obtenez le premier argument passĂ© Ă  notre jeu et analysez-le comme un entier non signĂ© i.
  • Si l’analyse Ă©choue ou si aucun argument n’est transmis, par dĂ©faut, jouez Ă  notre jeu avec 15 comme entrĂ©e.
  • Enfin, jouez Ă  notre jeu avec le nouvel entier non signĂ© i analysĂ©.

Maintenant, nous pouvons jouer à notre jeu avec n’importe quel nombre! Utilisez cargo run suivi de -- pour passer des arguments à notre jeu :

$ 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

Et si nous oublions de fournir un numéro ou fournissons un numéro invalide :

$ 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

Wow, c’était des tests trĂšs approfondis! CI passe. Nos patrons sont ravis. ExpĂ©dions-le! 🚀

La fin


SpongeBob SquarePants Trois semaines plus tard
Meme C'est bien

🐰 
 la fin de votre carriĂšre peut-ĂȘtre ?


Rien que pour rire! Tout est en feu! đŸ”„

Au début, tout semblait aller bien. Et puis à 02h07 du matin le samedi, mon bip a sonné :

📟 Votre jeu est en feu! đŸ”„

AprĂšs me ĂȘtre prĂ©cipitĂ© hors du lit, j’ai essayĂ© de comprendre ce qui se passait. J’ai essayĂ© de rechercher dans les journaux, mais c’était difficile parce que tout continuait de s’effondrer. Finalement, j’ai trouvĂ© le problĂšme. Les enfants ! Ils adorent notre jeu tellement, qu’ils jouaient jusqu’à un million! Dans un Ă©clair de gĂ©nie, j’ai ajoutĂ© deux nouveaux points de rĂ©fĂ©rence :

#[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 pour jouer au jeu avec le nombre cent (100).
  • Un micro-benchmark bench_play_game_1_000_000 pour jouer au jeu avec le nombre un million (1_000_000).

Lorsque je l’ai exĂ©cutĂ©, j’ai obtenu ceci :

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

Attendez un peu
 attendez un peu


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

Quoi ! 439 ns x 1,000 devrait ĂȘtre 439,000 ns pas 9,586,977 ns đŸ€Ż MĂȘme si j’ai correctement codĂ© ma sĂ©quence de Fibonacci, je dois avoir un bug de performance quelque part.

Corriger FizzBuzzFibonacci en Rust

Jetons un autre coup d’Ɠil à cette fonction 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
}

Maintenant que je pense Ă  la performance, je rĂ©alise que j’ai une boucle supplĂ©mentaire inutile. Nous pouvons complĂštement nous dĂ©barrasser de la boucle for i in 0..=n {} et il suffit de comparer la valeur current au nombre donnĂ© (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
}
  • Mettez Ă  jour votre fonction is_fibonacci_number.
  • Initialisez notre sĂ©quence de Fibonacci en commençant par 0 et 1 comme nombres previous et current respectivement.
  • ItĂ©rez tant que le numĂ©ro current est infĂ©rieur au nombre donnĂ© n.
  • Ajoutez le numĂ©ro previous et current pour obtenir le numĂ©ro next.
  • Mettez Ă  jour le numĂ©ro previous pour le numĂ©ro current.
  • Mettez Ă  jour le numĂ©ro current pour le numĂ©ro next.
  • Une fois que current est supĂ©rieur ou Ă©gal au nombre donnĂ© n, nous sortirons de la boucle.
  • VĂ©rifiez si le numĂ©ro current est Ă©gal au nombre donnĂ© n et renvoyez ce rĂ©sultat.

Maintenant, relançons ces benchmarks et voyons comment nous nous en sommes sortis :

$ 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, wow ! Notre benchmark bench_play_game est revenu Ă  peu prĂšs au niveau oĂč il Ă©tait pour le FizzBuzz original. J’aimerais me souvenir exactement de ce score. Mais ça fait trois semaines. Mon historique de terminal ne remonte pas si loin. Mais je pense que c’est proche !

Le benchmark bench_play_game_100 est descendu prĂšs de 10 fois, de 439 ns Ă  46 ns. Et le benchmark bench_play_game_1_000_000 est descendu plus de 10 000 fois ! De 9,586,977 ns Ă  53 ns !

🐰 HĂ©, au moins on a attrapĂ© ce bug de performance avant qu’il n’arrive en production
 oh, attendez. Jamais d’esprit


DĂ©tection des rĂ©gressions de performances dans l’intĂ©gration continue (CI)

Les dirigeants n’étaient pas contents du torrent de critiques nĂ©gatives que notre jeu a reçu Ă  cause de mon petit bug de performance. Ils m’ont dit de ne pas laisser cela se reproduire, et quand j’ai demandĂ© comment, ils m’ont juste dit de ne pas le refaire. Comment suis-je censĂ© gĂ©rer cela“

Heureusement, j’ai trouvĂ© cet outil open source gĂ©nial appelĂ© Bencher. Il y a un niveau gratuit super gĂ©nĂ©reux, donc je peux simplement utiliser Bencher Cloud pour mes projets personnels. Et au travail oĂč tout doit ĂȘtre dans notre cloud privĂ©, j’ai commencĂ© Ă  utiliser Bencher Self-Hosted.

Bencher a des adaptateurs intĂ©grĂ©s, il est donc facile de l’intĂ©grer dans CI. AprĂšs avoir suivi le guide de dĂ©marrage rapide, je suis en mesure d’exĂ©cuter mes benchmarks et de les suivre avec 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

En utilisant cet astucieux appareil de voyage dans le temps qu’un gentil lapin m’a donnĂ©, j’ai pu revenir en arriĂšre et revivre ce qui se serait passĂ© si nous utilisions Bencher depuis le dĂ©but. Vous pouvez voir oĂč nous avons d’abord poussĂ© l’implĂ©mentation buggĂ©e de FizzBuzzFibonacci. J’ai immĂ©diatement obtenu des Ă©checs dans CI en commentaire sur ma demande de tirage. Ce mĂȘme jour, j’ai corrigĂ© le bug de performance, en supprimant cette boucle inutile et supplĂ©mentaire. Pas de feux. Juste des utilisateurs heureux.

Bencher: Benchmarking Continu

🐰 Bencher

Bencher est une suite d’outils de benchmarking continu. Avez-vous dĂ©jĂ  eu une rĂ©gression de performance qui a impactĂ© vos utilisateurs ? Bencher aurait pu empĂȘcher cela de se produire. Bencher vous permet de dĂ©tecter et de prĂ©venir les rĂ©gressions de performance avant qu’elles n’arrivent en production.

  • ExĂ©cuter: ExĂ©cutez vos benchmarks localement ou en CI en utilisant vos outils de benchmarking prĂ©fĂ©rĂ©s. La CLI bencher enveloppe simplement votre harnais de benchmarking existant et stocke ses rĂ©sultats.
  • Suivre: Suivez les rĂ©sultats de vos benchmarks au fil du temps. Surveillez, interrogez et graphiquez les rĂ©sultats Ă  l’aide de la console web Bencher en fonction de la branche source, du banc d’essai et de la mesure.
  • DĂ©tecter: DĂ©tectez les rĂ©gressions de performances en CI. Bencher utilise des analyses de pointe et personnalisables pour dĂ©tecter les rĂ©gressions de performances avant qu’elles n’arrivent en production.

Pour les mĂȘmes raisons que les tests unitaires sont exĂ©cutĂ©s en CI pour prĂ©venir les rĂ©gressions de fonctionnalitĂ©s, les benchmarks devraient ĂȘtre exĂ©cutĂ©s en CI avec Bencher pour prĂ©venir les rĂ©gressions de performance. Les bugs de performance sont des bugs !

Commencez Ă  dĂ©tecter les rĂ©gressions de performances en CI — essayez Bencher Cloud gratuitement.

đŸ€– Ce document a Ă©tĂ© automatiquement gĂ©nĂ©rĂ© par OpenAI GPT-4. Il peut ne pas ĂȘtre prĂ©cis et peut contenir des erreurs. Si vous trouvez des erreurs, veuillez ouvrir une issue sur GitHub.