Comment est-il possible de comparer des programmes dans Rust? Par exemple, comment puis-je obtenir le temps d'exécution du programme en secondes?
Cette réponse est obsolète! Veuillez consulter les réponses ci-dessous pour des informations à jour.
Vous pouvez essayer de chronométrer des composants individuels dans le programme en utilisant caisse de temps .
Il pourrait être intéressant de noter 2 ans plus tard (pour aider les futurs programmeurs Rust qui trébuchent sur cette page) qu'il existe maintenant des outils pour comparer Rust code comme une partie de sa suite de tests.
(À partir du lien du guide ci-dessous) En utilisant le #[bench]
, on peut utiliser l'outillage standard Rust pour comparer les méthodes dans leur code.
extern crate test;
use test::Bencher;
#[bench]
fn bench_xor_1000_ints(b: &mut Bencher) {
b.iter(|| {
// use `test::black_box` to prevent compiler optimizations from disregarding
// unused values
test::black_box(range(0u, 1000).fold(0, |old, new| old ^ new));
});
}
Pour la commande cargo bench
cela produit quelque chose comme:
running 1 test
test bench_xor_1000_ints ... bench: 375 ns/iter (+/- 148)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
Liens:
Si vous voulez simplement chronométrer un morceau de code, vous pouvez utiliser la caisse time
. temps quant à lui déconseillé , cependant. Une caisse de suivi est chrono
.
Ajouter time = "*"
à ton Cargo.toml
.
Ajouter
extern crate time;
use time::PreciseTime;
avant votre fonction principale et
let start = PreciseTime::now();
// whatever you want to do
let end = PreciseTime::now();
println!("{} seconds for whatever you did.", start.to(end));
[package]
name = "hello_world" # the name of the package
version = "0.0.1" # the current version, obeying semver
authors = [ "[email protected]" ]
[[bin]]
name = "Rust"
path = "Rust.rs"
[dependencies]
Rand = "*" # Or a specific version
time = "*"
extern crate Rand;
extern crate time;
use Rand::Rng;
use time::PreciseTime;
fn main() {
// Creates an array of 10000000 random integers in the range 0 - 1000000000
//let mut array: [i32; 10000000] = [0; 10000000];
let n = 10000000;
let mut array = Vec::new();
// Fill the array
let mut rng = Rand::thread_rng();
for _ in 0..n {
//array[i] = rng.gen::<i32>();
array.Push(rng.gen::<i32>());
}
// Sort
let start = PreciseTime::now();
array.sort();
let end = PreciseTime::now();
println!("{} seconds for sorting {} integers.", start.to(end), n);
}
Pour les tests de synchronisation, vous pouvez utiliser std::time::Instant
fn my_function() {
use std::time::Instant;
let now = Instant::now();
{
my_function_to_measure();
}
let elapsed = now.elapsed();
let sec = (elapsed.as_secs() as f64) + (elapsed.subsec_nanos() as f64 / 1000_000_000.0);
println!("Seconds: {}", sec);
}
fn main() {
my_function();
}
Bien sûr, si vous le faisiez souvent, vous voudriez généraliser la conversion, ou utiliser une caisse qui fournit des utilitaires pour cela, ou envelopper Instant
et Duration
dans vos propres fonctions afin qu'elles puissent être écrites en d'une manière moins verbeuse.
Actuellement, il n'y a pas d'interface avec les fonctions Linux suivantes:
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)
getrusage
times
(page de manuel: man 2 times
)Les moyens disponibles pour mesurer le temps CPU et les hotspots d'un programme Rust sous Linux sont:
/usr/bin/time program
perf stat program
perf record --freq 100000 program; perf report
valgrind --tool=callgrind program; kcachegrind callgrind.out.*
La sortie de perf report
et valgrind
dépend de la disponibilité des informations de débogage dans le programme. Cela peut ne pas fonctionner.
J'ai créé une petite caisse pour cela ( measure_time ), qui enregistre ou imprime le temps jusqu'à la fin de la portée.
#[macro_use]
extern crate measure_time;
fn main() {
print_time!("measure function");
do_stuff();
}
Il existe plusieurs façons pour évaluer votre programme Rust. Pour la plupart des tests réels, vous devez utiliser un cadre d'analyse comparative approprié, car ils aident avec quelques éléments qui sont facile à visser (y compris l'analyse statistique) Veuillez lire également la section "Pourquoi écrire des repères est difficile" tout en bas!
Instant
et Duration
de la bibliothèque standardPour vérifier rapidement la durée d'exécution d'un morceau de code, vous pouvez utiliser les types dans std::time
. Le module est assez minimal, mais il convient aux mesures de temps simples. Vous devez utiliser Instant
au lieu de SystemTime
car la première est une horloge à augmentation monotone et la seconde ne l'est pas. Exemple ( Aire de jeux ):
use std::time::Instant;
let before = Instant::now();
workload();
println!("Elapsed time: {:.2?}", before.elapsed());
La précision de Instant
de std n'est malheureusement pas spécifiée dans la documentation, mais sur tous les principaux systèmes d'exploitation, elle utilise la meilleure précision que la plate-forme peut fournir (elle est généralement d'environ 20 ns).
Si std::time
n'offre pas suffisamment de fonctionnalités pour votre cas, vous pouvez jeter un œil à chrono
. Cependant, pour mesurer les durées, il est peu probable que vous ayez besoin de cette caisse externe.
L'utilisation de frameworks est souvent une bonne idée, car ils essaient de vous empêcher de faire des erreurs spécifiques.
La rouille a une fonction d'analyse comparative intégrée pratique, qui est malheureusement toujours instable en 2019-07. Vous devez ajouter le #[bench]
attribuer à votre fonction et lui faire accepter un &mut test::Bencher
argument:
#![feature(test)]
extern crate test;
use test::Bencher;
#[bench]
fn bench_workload(b: &mut Bencher) {
b.iter(|| workload());
}
Exécution de cargo bench
affichera:
running 1 test
test bench_workload ... bench: 78,534 ns/iter (+/- 3,606)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out
La caisse criterion
est un framework qui fonctionne sur stable, mais il est un peu plus compliqué que la solution intégrée. Il fait une analyse statistique plus sophistiquée, offre une API plus riche, produit plus d'informations et peut même générer automatiquement des graphiques.
Voir la section "Démarrage rapide" pour plus d'informations sur l'utilisation de Criterion.
Il existe de nombreux pièges lors de l'écriture de repères. Une seule erreur ne peut que rendre vos résultats de référence dénués de sens. Voici une liste d'erreurs courantes:
rustc -O3
ou cargo build --release
. Lorsque vous exécutez vos benchmarks avec cargo bench
, Cargo activera automatiquement les optimisations. Cette étape est importante car il existe souvent une grande différence de performances entre le code optimisé et non optimisé Rust code. Assurez-vous que votre benchmark n'est pas complètement supprimé : les benchmarks sont par nature très artificiels. Habituellement, le résultat de votre charge de travail n'est pas inspecté car vous souhaitez uniquement mesurer la durée. Cependant, cela signifie qu'un bon optimiseur pourrait supprimer tout votre benchmark car il n'a pas d'effets secondaires (enfin, à part le temps qui passe). Donc, pour tromper l'optimiseur, vous devez en quelque sorte utiliser votre valeur de résultat afin que votre charge de travail ne puisse pas être supprimée. Un moyen simple consiste à imprimer le résultat. Une meilleure solution est quelque chose comme black_box
. Cette fonction masque fondamentalement une valeur de LLVM en ce que LLVM ne peut pas savoir ce qui se passera avec la valeur. Rien ne se passe, mais LLVM ne sait pas. C'est le but.
De bons cadres d'analyse comparative utilisent une boîte à blocs dans plusieurs situations. Par exemple, la fermeture donnée à la méthode iter
(pour les deux, intégrée et Criterion Bencher
) peut renvoyer une valeur. Cette valeur est automatiquement transmise dans un black_box
.
black_box
pour éviter une optimisation trop agressive de LLVM.