É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>());
}
#}