Écrire un benchmark
L'API de benchmark de Rust est un work-in-progress et demande donc
l'utilisation d'une version instable (nightly) du compilateur. Vous pouvez
l'installer avec: rustup install nightly
, et activer la version nightly du
compilateur avec rustup default nightly
. Attention, c'est un changement de
configuration global. Alternativement vous pouvez remplacer vos appels à
cargo
par rustup run nightly cargo
Note: Il existe une librairies rustc-test
qui permet d'utiliser cette API
avec les versions stables de Rust; malheureusement, elle ne fonctionne plus
depuis rust 1.24.
On peux ensuite écrire un benchmark en créant une fonction précédée de l'attribut
[bench]
qui prend en argument une référence mutable vers un Bencher
, comme dans
l'exemple ci-dessous. Le code à évaluer doit être donné en argument à la méthode
Bencher::iter
. Cette méthode exécute plusieurs fois la clôture donnée en argument et
affiche le temps d'exécution moyen dans la console.
Attention: il faut indiquer #![feature(test)]
et extern crate test
dans
le fichier main.rs
du projet. Le #![feature(test)]
indique à Rust d'activer
les APIs expérimentales du module de test, dont l'API de benchmark.
# #![allow(unused_variables)] #![feature(test)] #fn main() { extern crate test; use test::Bencher; /// Adds two vectors fn add_vec(lhs: &Vec<u32>, rhs: &Vec<u32>) -> Vec<u32> { lhs.iter().zip(rhs).map(|(lhs, rhs)| lhs+rhs).collect() } /// A benchmark for the `add_vec` function. #[bench] fn bench_add_vec(bencher: &mut Bencher) { // Initialize the benchmark data. let input_lhs = vec![1, 2, 3, 4, 5]; let input_rhs = vec![5, 4, 3, 2, 1]; // Let the bencher compute the average execution time. bencher.iter(|| add_vec(&input_lhs, &input_rhs)); } #}
Pour compiler et exécuter tous les benchmarks, il suffit de lancer la commande
cargo bench
(ou rustup run nightly cargo bench
).
Partager les données entre les benchmarks
Il est parfois utile de partager des données lourdes à initialiser entre plusieurs benchmark. Pour cela, il faut déclarer une variable statique (équivalent de globale en C). Le problème est que des variables statiques ne peuvent être initialisées qu'avec des constantes. Pour contourner ce problème, on peut utiliser la bibliothèque lazy static qui fournis une macro pour déclarer des variables statiques qui s'initialisent lors du premier accès.
Pour cela, il faut ajouter lazy_static = "1.1"
comme dépendence au fichier Cargo.toml
puis déclarer les variables comme suit.
# #![allow(unused_variables)] #fn main() { #[macro_use] extern crate lazy_static; lazy_static! { static ref COSTLY_DATA: Vec<usize> = { // Initialisation de COSTLY_DATA (0..1000).map(|i| i*i).collect() }; } /// Exemple d'utilisation #[bench] fn use_data(bencher: &mut Bencher) { bencher.iter(|| COSTLY_DATA.iter().sum::<usize>()); } #}