1 //! Rayon extensions for `HashTable`. 2 3 use super::raw::{RawIntoParIter, RawParDrain, RawParIter}; 4 use crate::hash_table::HashTable; 5 use crate::raw::{Allocator, Global}; 6 use core::fmt; 7 use core::marker::PhantomData; 8 use rayon::iter::plumbing::UnindexedConsumer; 9 use rayon::iter::{IntoParallelIterator, ParallelIterator}; 10 11 /// Parallel iterator over shared references to entries in a map. 12 /// 13 /// This iterator is created by the [`par_iter`] method on [`HashTable`] 14 /// (provided by the [`IntoParallelRefIterator`] trait). 15 /// See its documentation for more. 16 /// 17 /// [`par_iter`]: /hashbrown/struct.HashTable.html#method.par_iter 18 /// [`HashTable`]: /hashbrown/struct.HashTable.html 19 /// [`IntoParallelRefIterator`]: https://docs.rs/rayon/1.0/rayon/iter/trait.IntoParallelRefIterator.html 20 pub struct ParIter<'a, T> { 21 inner: RawParIter<T>, 22 marker: PhantomData<&'a T>, 23 } 24 25 impl<'a, T: Sync> ParallelIterator for ParIter<'a, T> { 26 type Item = &'a T; 27 28 #[cfg_attr(feature = "inline-more", inline)] drive_unindexed<C>(self, consumer: C) -> C::Result where C: UnindexedConsumer<Self::Item>,29 fn drive_unindexed<C>(self, consumer: C) -> C::Result 30 where 31 C: UnindexedConsumer<Self::Item>, 32 { 33 self.inner 34 .map(|x| unsafe { x.as_ref() }) 35 .drive_unindexed(consumer) 36 } 37 } 38 39 impl<T> Clone for ParIter<'_, T> { 40 #[cfg_attr(feature = "inline-more", inline)] clone(&self) -> Self41 fn clone(&self) -> Self { 42 Self { 43 inner: self.inner.clone(), 44 marker: PhantomData, 45 } 46 } 47 } 48 49 impl<T: fmt::Debug> fmt::Debug for ParIter<'_, T> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 51 let iter = unsafe { self.inner.iter() }.map(|x| unsafe { x.as_ref() }); 52 f.debug_list().entries(iter).finish() 53 } 54 } 55 56 /// Parallel iterator over mutable references to entries in a map. 57 /// 58 /// This iterator is created by the [`par_iter_mut`] method on [`HashTable`] 59 /// (provided by the [`IntoParallelRefMutIterator`] trait). 60 /// See its documentation for more. 61 /// 62 /// [`par_iter_mut`]: /hashbrown/struct.HashTable.html#method.par_iter_mut 63 /// [`HashTable`]: /hashbrown/struct.HashTable.html 64 /// [`IntoParallelRefMutIterator`]: https://docs.rs/rayon/1.0/rayon/iter/trait.IntoParallelRefMutIterator.html 65 pub struct ParIterMut<'a, T> { 66 inner: RawParIter<T>, 67 marker: PhantomData<&'a mut T>, 68 } 69 70 impl<'a, T: Send> ParallelIterator for ParIterMut<'a, T> { 71 type Item = &'a mut T; 72 73 #[cfg_attr(feature = "inline-more", inline)] drive_unindexed<C>(self, consumer: C) -> C::Result where C: UnindexedConsumer<Self::Item>,74 fn drive_unindexed<C>(self, consumer: C) -> C::Result 75 where 76 C: UnindexedConsumer<Self::Item>, 77 { 78 self.inner 79 .map(|x| unsafe { x.as_mut() }) 80 .drive_unindexed(consumer) 81 } 82 } 83 84 impl<T: fmt::Debug> fmt::Debug for ParIterMut<'_, T> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 86 ParIter { 87 inner: self.inner.clone(), 88 marker: PhantomData, 89 } 90 .fmt(f) 91 } 92 } 93 94 /// Parallel iterator over entries of a consumed map. 95 /// 96 /// This iterator is created by the [`into_par_iter`] method on [`HashTable`] 97 /// (provided by the [`IntoParallelIterator`] trait). 98 /// See its documentation for more. 99 /// 100 /// [`into_par_iter`]: /hashbrown/struct.HashTable.html#method.into_par_iter 101 /// [`HashTable`]: /hashbrown/struct.HashTable.html 102 /// [`IntoParallelIterator`]: https://docs.rs/rayon/1.0/rayon/iter/trait.IntoParallelIterator.html 103 pub struct IntoParIter<T, A: Allocator = Global> { 104 inner: RawIntoParIter<T, A>, 105 } 106 107 impl<T: Send, A: Allocator + Send> ParallelIterator for IntoParIter<T, A> { 108 type Item = T; 109 110 #[cfg_attr(feature = "inline-more", inline)] drive_unindexed<C>(self, consumer: C) -> C::Result where C: UnindexedConsumer<Self::Item>,111 fn drive_unindexed<C>(self, consumer: C) -> C::Result 112 where 113 C: UnindexedConsumer<Self::Item>, 114 { 115 self.inner.drive_unindexed(consumer) 116 } 117 } 118 119 impl<T: fmt::Debug, A: Allocator> fmt::Debug for IntoParIter<T, A> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 121 ParIter { 122 inner: unsafe { self.inner.par_iter() }, 123 marker: PhantomData, 124 } 125 .fmt(f) 126 } 127 } 128 129 /// Parallel draining iterator over entries of a map. 130 /// 131 /// This iterator is created by the [`par_drain`] method on [`HashTable`]. 132 /// See its documentation for more. 133 /// 134 /// [`par_drain`]: /hashbrown/struct.HashTable.html#method.par_drain 135 /// [`HashTable`]: /hashbrown/struct.HashTable.html 136 pub struct ParDrain<'a, T, A: Allocator = Global> { 137 inner: RawParDrain<'a, T, A>, 138 } 139 140 impl<T: Send, A: Allocator + Sync> ParallelIterator for ParDrain<'_, T, A> { 141 type Item = T; 142 143 #[cfg_attr(feature = "inline-more", inline)] drive_unindexed<C>(self, consumer: C) -> C::Result where C: UnindexedConsumer<Self::Item>,144 fn drive_unindexed<C>(self, consumer: C) -> C::Result 145 where 146 C: UnindexedConsumer<Self::Item>, 147 { 148 self.inner.drive_unindexed(consumer) 149 } 150 } 151 152 impl<T: fmt::Debug, A: Allocator> fmt::Debug for ParDrain<'_, T, A> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 154 ParIter { 155 inner: unsafe { self.inner.par_iter() }, 156 marker: PhantomData, 157 } 158 .fmt(f) 159 } 160 } 161 162 impl<T: Send, A: Allocator> HashTable<T, A> { 163 /// Consumes (potentially in parallel) all values in an arbitrary order, 164 /// while preserving the map's allocated memory for reuse. 165 #[cfg_attr(feature = "inline-more", inline)] par_drain(&mut self) -> ParDrain<'_, T, A>166 pub fn par_drain(&mut self) -> ParDrain<'_, T, A> { 167 ParDrain { 168 inner: self.raw.par_drain(), 169 } 170 } 171 } 172 173 impl<T: Send, A: Allocator + Send> IntoParallelIterator for HashTable<T, A> { 174 type Item = T; 175 type Iter = IntoParIter<T, A>; 176 177 #[cfg_attr(feature = "inline-more", inline)] into_par_iter(self) -> Self::Iter178 fn into_par_iter(self) -> Self::Iter { 179 IntoParIter { 180 inner: self.raw.into_par_iter(), 181 } 182 } 183 } 184 185 impl<'a, T: Sync, A: Allocator> IntoParallelIterator for &'a HashTable<T, A> { 186 type Item = &'a T; 187 type Iter = ParIter<'a, T>; 188 189 #[cfg_attr(feature = "inline-more", inline)] into_par_iter(self) -> Self::Iter190 fn into_par_iter(self) -> Self::Iter { 191 ParIter { 192 inner: unsafe { self.raw.par_iter() }, 193 marker: PhantomData, 194 } 195 } 196 } 197 198 impl<'a, T: Send, A: Allocator> IntoParallelIterator for &'a mut HashTable<T, A> { 199 type Item = &'a mut T; 200 type Iter = ParIterMut<'a, T>; 201 202 #[cfg_attr(feature = "inline-more", inline)] into_par_iter(self) -> Self::Iter203 fn into_par_iter(self) -> Self::Iter { 204 ParIterMut { 205 inner: unsafe { self.raw.par_iter() }, 206 marker: PhantomData, 207 } 208 } 209 } 210 211 #[cfg(test)] 212 mod test_par_table { 213 use alloc::vec::Vec; 214 use core::sync::atomic::{AtomicUsize, Ordering}; 215 216 use rayon::prelude::*; 217 218 use crate::{hash_map::make_hash, hash_table::HashTable, DefaultHashBuilder}; 219 220 #[test] test_iterate()221 fn test_iterate() { 222 let hasher = DefaultHashBuilder::default(); 223 let mut a = HashTable::new(); 224 for i in 0..32 { 225 a.insert_unique(make_hash(&hasher, &i), i, |x| make_hash(&hasher, x)); 226 } 227 let observed = AtomicUsize::new(0); 228 a.par_iter().for_each(|k| { 229 observed.fetch_or(1 << *k, Ordering::Relaxed); 230 }); 231 assert_eq!(observed.into_inner(), 0xFFFF_FFFF); 232 } 233 234 #[test] test_move_iter()235 fn test_move_iter() { 236 let hasher = DefaultHashBuilder::default(); 237 let hs = { 238 let mut hs = HashTable::new(); 239 240 hs.insert_unique(make_hash(&hasher, &'a'), 'a', |x| make_hash(&hasher, x)); 241 hs.insert_unique(make_hash(&hasher, &'b'), 'b', |x| make_hash(&hasher, x)); 242 243 hs 244 }; 245 246 let v = hs.into_par_iter().collect::<Vec<char>>(); 247 assert!(v == ['a', 'b'] || v == ['b', 'a']); 248 } 249 } 250