Skip to content

Commit

Permalink
Initial rayon support: ParallelExtend + FromParallelIterator
Browse files Browse the repository at this point in the history
This was a straightforward step towards jonhoo#14
  • Loading branch information
Others committed May 26, 2020
1 parent 448d9f6 commit 487e9d3
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Don't include build output
/target
**/*.rs.bk
# We're a library--don't include our lockfile
Cargo.lock
# Don't include rustfmt backup files
**/*.rs.bk

# Don't include IDE configuration files
.idea
**/.vscode/*
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ sanitize = ['crossbeam-epoch/sanitize']
crossbeam-epoch = "0.8.2"
parking_lot = "0.10"
num_cpus = "1.12.0"
rayon = {version = "1.3", optional = true}
serde = {version = "1.0.105", optional = true}

[dependencies.ahash]
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ mod raw;
mod set;
mod set_ref;

#[cfg(feature = "rayon")]
mod rayon_impls;

#[cfg(feature = "serde")]
mod serde_impls;

Expand Down
119 changes: 119 additions & 0 deletions src/rayon_impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::HashMap;
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelExtend, ParallelIterator};
use std::hash::{BuildHasher, Hash};

impl<K, V, S> ParallelExtend<(K, V)> for HashMap<K, V, S>
where
K: Clone + Hash + Ord + Send + Sync + 'static,
V: Send + Sync + 'static,
S: BuildHasher + Sync,
{
// This is of limited use due to the `&mut self` parameter. See `par_extend_sync`
fn par_extend<I>(&mut self, par_iter: I)
where
I: IntoParallelIterator<Item = (K, V)>,
{
self.par_extend_sync(par_iter);
}
}

impl<K, V, S> HashMap<K, V, S>
where
K: Clone + Hash + Ord + Send + Sync + 'static,
V: Send + Sync + 'static,
S: BuildHasher + Sync,
{
// FIXME: Terrible name, just didn't want to shadow the rayon name
fn par_extend_sync<I>(&self, par_iter: I)
where
I: IntoParallelIterator<Item = (K, V)>,
{
par_iter.into_par_iter().for_each(|(k, v)| {
// Unfortunate that we need to create a guard for each insert operation
// Ideally we'd create a guard for each `rayon` worker thread instead
// Perhaps this could be done with a thread local?
let guard = self.guard();
self.insert(k, v, &guard);
});
}
}

impl<K, V> FromParallelIterator<(K, V)> for HashMap<K, V, crate::DefaultHashBuilder>
where
K: Clone + Hash + Ord + Send + Sync + 'static,
V: Send + Sync + 'static,
{
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = (K, V)>,
{
let mut created_map = HashMap::new();
created_map.par_extend(par_iter);
created_map
}
}

#[cfg(test)]
mod test {
use crate::HashMap;
use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelExtend};

#[test]
fn parallel_extend_by_nothing() {
let to_extend_with = Vec::new();

let mut map = HashMap::new();
let guard = map.guard();
map.insert(1, 2, &guard);
map.insert(3, 4, &guard);

map.par_extend(to_extend_with.into_par_iter());

assert_eq!(map.len(), 2);

assert_eq!(map.get(&1, &guard), Some(&2));
assert_eq!(map.get(&3, &guard), Some(&4));
}

#[test]
fn parallel_extend_by_a_bunch() {
let mut to_extend_with = Vec::new();
for i in 0..100 {
to_extend_with.push((i + 100, i * 10));
}

let mut map = HashMap::new();
let guard = map.guard();
map.insert(1, 2, &guard);
map.insert(3, 4, &guard);

map.par_extend(to_extend_with.into_par_iter());
assert_eq!(map.len(), 102);

assert_eq!(map.get(&1, &guard), Some(&2));
assert_eq!(map.get(&3, &guard), Some(&4));
assert_eq!(map.get(&100, &guard), Some(&0));
assert_eq!(map.get(&199, &guard), Some(&990));
}

#[test]
fn from_empty_parallel_iter() {
let to_create_from: Vec<(i32, i32)> = Vec::new();
let created_map: HashMap<i32, i32> = HashMap::from_par_iter(to_create_from.into_par_iter());
assert_eq!(created_map.len(), 0);
}

#[test]
fn from_large_parallel_iter() {
let mut to_create_from: Vec<(i32, i32)> = Vec::new();
for i in 0..100 {
to_create_from.push((i + 100, i * 10));
}
let created_map: HashMap<i32, i32> = HashMap::from_par_iter(to_create_from.into_par_iter());
assert_eq!(created_map.len(), 100);

let guard = created_map.guard();
assert_eq!(created_map.get(&100, &guard), Some(&0));
assert_eq!(created_map.get(&199, &guard), Some(&990));
}
}

0 comments on commit 487e9d3

Please sign in to comment.