par_wordcount.rs en Rust
Frecuencias de palabras sobre un corpus, en serie y en paralelo, con los tiempos comparados.
use std::collections::HashMap;
use std::time::Instant;
use rayon::prelude::*;
fn corpus(copies: usize) -> Vec<String> {
let base = [
"the quick brown fox jumps over the lazy dog",
"practice makes the difference between typing and thinking",
"ownership borrowing lifetimes traits iterators errors",
];
base.iter()
.cycle()
.take(base.len() * copies)
.map(|line| line.to_string())
.collect()
}
fn serial_counts(lines: &[String]) -> HashMap<&str, u32> {
let mut counts = HashMap::new();
for line in lines {
for word in line.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
}
counts
}
fn parallel_counts(lines: &[String]) -> HashMap<&str, u32> {
lines
.par_iter()
.fold(HashMap::new, |mut acc: HashMap<&str, u32>, line| {
for word in line.split_whitespace() {
*acc.entry(word).or_insert(0) += 1;
}
acc
})
.reduce(HashMap::new, |mut a, b| {
for (word, n) in b {
*a.entry(word).or_insert(0) += n;
}
a
})
}
fn top(counts: &HashMap<&str, u32>, n: usize) -> Vec<(String, u32)> {
let mut pairs: Vec<(String, u32)> =
counts.iter().map(|(w, c)| (w.to_string(), *c)).collect();
pairs.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
pairs.into_iter().take(n).collect()
}
fn main() {
let lines = corpus(20_000);
println!("{} lines, {} threads", lines.len(),
rayon::current_num_threads());
let started = Instant::now();
let one = serial_counts(&lines);
let serial_time = started.elapsed();
let started = Instant::now();
let many = parallel_counts(&lines);
let parallel_time = started.elapsed();
assert_eq!(one, many);
println!("serial {:?}", serial_time);
println!("parallel {:?}", parallel_time);
println!(
"speedup {:.2}x",
serial_time.as_secs_f64() / parallel_time.as_secs_f64()
);
for (word, count) in top(&many, 5) {
println!("{count:>7} {word}");
}
}
Cómo funciona
- El camino paralelo pliega un mapa por hilo y luego los fusiona.
- Ambos caminos producen la misma tabla, y el código lo afirma.
- Los tiempos se imprimen lado a lado para que la ganancia se vea.
Palabras clave y builtins usados aquí
StringVecfnforinletmutstru32useusize
El intento, en números
- Líneas
- 77
- Caracteres a escribir
- 1770
- Tokens
- 548
- Ritmo de tres estrellas
- 115 tpm
Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 286 segundos.
Paso 1 de 1 en Bis; paso 9 de 9 en Paralelismo de datos con rayon.