Profilage du code Rust : trouver les goulots d'étranglement en pratique
Lorsque vous écrivez en Rust, la performance est souvent un avantage clé. Mais même le code le plus rapide peut avoir des goulots d'étranglement cachés : allocations lentes, algorithmes inefficaces ou problèmes de cache. Le profilage est le processus de mesure des performances d'un programme pour identifier ces zones. Dans cet article, nous allons explorer les outils et méthodes pratiques de profilage du code Rust, des simples benchmarks à la visualisation flamegraph.
Pourquoi profiler le code Rust ?
Rust garantit la sécurité mémoire, mais ne garantit pas une performance optimale. Les goulots d'étranglement peuvent provenir de :
- Structures de données inefficaces (par exemple, utiliser
Vecau lieu deHashMappour des recherches fréquentes). - Allocations excessives (clonage de chaînes ou vecteurs là où des références pourraient être utilisées).
- Problèmes de cache (mauvais ordre de parcours des matrices).
- Verrous dans le code multithread (contention sur les mutex).
Le profilage permet de trouver exactement les parties à optimiser, plutôt que de deviner.
Outils pour le profilage Rust
Examinons trois approches principales : les benchmarks intégrés, les profileurs système et la visualisation.
1. Benchmarks avec criterion
La bibliothèque criterion est la norme pour les micro-benchmarks en Rust. Elle permet de mesurer le temps d'exécution de fonctions individuelles, en tenant compte des valeurs aberrantes statistiques.
Installation dans Cargo.toml :
[dev-dependencies]criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]name = "my_benchmark"harness = falseExemple de benchmark (fichier benches/my_benchmark.rs) :
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 { match n { 0 => 1, 1 => 1, n => fibonacci(n-1) + fibonacci(n-2), }}
fn benchmark_fib(c: &mut Criterion) { c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));}
criterion_group!(benches, benchmark_fib);criterion_main!(benches);Exécution : cargo bench. Criterion affichera le temps moyen, l'écart type et générera un rapport HTML avec des graphiques.
2. Profileur système perf (Linux)
perf est un outil puissant du noyau Linux pour collecter des statistiques processeur (cycles, échecs de cache, instructions). Pour Rust, il est particulièrement utile car il montre non seulement le temps, mais aussi les événements matériels.
Exemple d'utilisation :
# Collecte de donnéesperf record -g -- ./target/release/my_program
# Analyse en mode interactifperf report -g
# Visualisation des fonctions chaudesperf top -p $(pgrep my_program)Le flag -g active l'enregistrement de la pile d'appels (call graph). Cela permet de voir quelles fonctions appellent le code lent.
3. Visualisation avec flamegraph
Flamegraph (graphiques en flammes) est une méthode de visualisation de la pile d'appels. La largeur d'un rectangle indique combien de temps le programme a passé dans cette fonction et ses descendants. Plus le bloc est large, plus le temps est important.
Installation de l'outil :
cargo install flamegraphExécution :
cargo flamegraph --bin my_programAprès exécution, vous obtiendrez un fichier flamegraph.svg. Ouvrez-le dans un navigateur — vous verrez un graphique interactif où vous pouvez cliquer sur les blocs pour plus de détails.
Exemple pratique : recherche d'un goulot d'étranglement
Supposons que nous ayons un programme qui traite un grand fichier texte et compte la fréquence des mots. Le code pourrait ressembler à ceci :
use std::collections::HashMap;use std::fs::File;use std::io::{Buf