Counting in parallel in Rust
Merging per-thread maps beats fighting over one shared lock.
use rayon::prelude::*;
use std::collections::HashMap;
fn main() {
let words: Vec<&str> = "the quick brown fox the lazy dog the end"
.split_whitespace()
.collect();
let counts = words
.par_iter()
.copied()
.fold(HashMap::new, |mut acc: HashMap<&str, u32>, word| {
*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
});
let mut pairs: Vec<(&&str, &u32)> = counts.iter().collect();
pairs.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
println!("{:?}", &pairs[..3]);
}
How it works
foldbuilds a map per thread with no contention.reducemerges the partial maps at the end.- A single
Mutex<HashMap>would serialize the whole run.
Keywords and builtins used here
HashMapVecfnforinletmainmutstru32use
The run, in numbers
- Lines
- 26
- Characters to type
- 555
- Tokens
- 212
- Three-star pace
- 110 tpm
At the three-star pace of 110 tokens a minute, this run takes about 116 seconds.
Step 2 of 2 in Reductions, step 5 of 9 in Data parallelism with rayon.