Thanks to visit codestin.com
Credit goes to docs.rs

ndarray/parallel/
par.rs

1use rayon::iter::plumbing::bridge;
2use rayon::iter::plumbing::bridge_unindexed;
3use rayon::iter::plumbing::Folder;
4use rayon::iter::plumbing::Producer;
5use rayon::iter::plumbing::ProducerCallback;
6use rayon::iter::plumbing::UnindexedProducer;
7use rayon::iter::plumbing::{Consumer, UnindexedConsumer};
8use rayon::iter::IndexedParallelIterator;
9use rayon::iter::ParallelIterator;
10use rayon::prelude::IntoParallelIterator;
11
12use crate::iter::AxisChunksIter;
13use crate::iter::AxisChunksIterMut;
14use crate::iter::AxisIter;
15use crate::iter::AxisIterMut;
16use crate::split_at::SplitPreference;
17use crate::Dimension;
18use crate::{ArrayView, ArrayViewMut};
19
20/// Parallel iterator wrapper.
21#[derive(Copy, Clone, Debug)]
22pub struct Parallel<I>
23{
24    iter: I,
25    min_len: usize,
26}
27
28const DEFAULT_MIN_LEN: usize = 1;
29
30/// Parallel producer wrapper.
31#[derive(Copy, Clone, Debug)]
32struct ParallelProducer<I>(I, usize);
33
34macro_rules! par_iter_wrapper {
35    // thread_bounds are either Sync or Send + Sync
36    ($iter_name:ident, [$($thread_bounds:tt)*]) => {
37    /// Requires crate feature `rayon`.
38    impl<'a, A, D> IntoParallelIterator for $iter_name<'a, A, D>
39        where D: Dimension,
40              A: $($thread_bounds)*,
41    {
42        type Item = <Self as Iterator>::Item;
43        type Iter = Parallel<Self>;
44        fn into_par_iter(self) -> Self::Iter {
45            Parallel {
46                iter: self,
47                min_len: DEFAULT_MIN_LEN,
48            }
49        }
50    }
51
52    impl<'a, A, D> ParallelIterator for Parallel<$iter_name<'a, A, D>>
53        where D: Dimension,
54              A: $($thread_bounds)*,
55    {
56        type Item = <$iter_name<'a, A, D> as Iterator>::Item;
57        fn drive_unindexed<C>(self, consumer: C) -> C::Result
58            where C: UnindexedConsumer<Self::Item>
59        {
60            bridge(self, consumer)
61        }
62
63        fn opt_len(&self) -> Option<usize> {
64            Some(self.iter.len())
65        }
66    }
67
68    impl<'a, A, D> IndexedParallelIterator for Parallel<$iter_name<'a, A, D>>
69        where D: Dimension,
70              A: $($thread_bounds)*,
71    {
72        fn with_producer<Cb>(self, callback: Cb) -> Cb::Output
73            where Cb: ProducerCallback<Self::Item>
74        {
75            callback.callback(ParallelProducer(self.iter, self.min_len))
76        }
77
78        fn len(&self) -> usize {
79            ExactSizeIterator::len(&self.iter)
80        }
81
82        fn drive<C>(self, consumer: C) -> C::Result
83            where C: Consumer<Self::Item>
84        {
85            bridge(self, consumer)
86        }
87    }
88
89    impl<'a, A, D> IntoIterator for ParallelProducer<$iter_name<'a, A, D>>
90        where D: Dimension,
91    {
92        type IntoIter = $iter_name<'a, A, D>;
93        type Item = <Self::IntoIter as Iterator>::Item;
94
95        fn into_iter(self) -> Self::IntoIter {
96            self.0
97        }
98    }
99
100    // This is the real magic, I guess
101    impl<'a, A, D> Producer for ParallelProducer<$iter_name<'a, A, D>>
102        where D: Dimension,
103              A: $($thread_bounds)*,
104    {
105        type IntoIter = $iter_name<'a, A, D>;
106        type Item = <Self::IntoIter as Iterator>::Item;
107
108        fn into_iter(self) -> Self::IntoIter {
109            self.0
110        }
111
112        fn split_at(self, i: usize) -> (Self, Self) {
113            let (a, b) = self.0.split_at(i);
114            (ParallelProducer(a, self.1), ParallelProducer(b, self.1))
115        }
116    }
117
118    };
119}
120
121par_iter_wrapper!(AxisIter, [Sync]);
122par_iter_wrapper!(AxisIterMut, [Send + Sync]);
123par_iter_wrapper!(AxisChunksIter, [Sync]);
124par_iter_wrapper!(AxisChunksIterMut, [Send + Sync]);
125
126macro_rules! par_iter_view_wrapper {
127    // thread_bounds are either Sync or Send + Sync
128    ($view_name:ident, [$($thread_bounds:tt)*]) => {
129    /// Requires crate feature `rayon`.
130    impl<'a, A, D> IntoParallelIterator for $view_name<'a, A, D>
131        where D: Dimension,
132              A: $($thread_bounds)*,
133    {
134        type Item = <Self as IntoIterator>::Item;
135        type Iter = Parallel<Self>;
136        fn into_par_iter(self) -> Self::Iter {
137            Parallel {
138                iter: self,
139                min_len: DEFAULT_MIN_LEN,
140            }
141        }
142    }
143
144    impl<'a, A, D> ParallelIterator for Parallel<$view_name<'a, A, D>>
145        where D: Dimension,
146              A: $($thread_bounds)*,
147    {
148        type Item = <$view_name<'a, A, D> as IntoIterator>::Item;
149        fn drive_unindexed<C>(self, consumer: C) -> C::Result
150            where C: UnindexedConsumer<Self::Item>
151        {
152            bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer)
153        }
154
155        fn opt_len(&self) -> Option<usize> {
156            None
157        }
158    }
159
160    impl<'a, A, D> Parallel<$view_name<'a, A, D>>
161        where D: Dimension,
162              A: $($thread_bounds)*,
163    {
164        /// Sets the minimum number of elements desired to process in each job. This will not be
165        /// split any smaller than this length, but of course a producer could already be smaller
166        /// to begin with.
167        ///
168        /// ***Panics*** if `min_len` is zero.
169        pub fn with_min_len(self, min_len: usize) -> Self {
170            assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks.");
171
172            Self {
173                min_len,
174                ..self
175            }
176        }
177    }
178
179    impl<'a, A, D> UnindexedProducer for ParallelProducer<$view_name<'a, A, D>>
180        where D: Dimension,
181              A: $($thread_bounds)*,
182    {
183        type Item = <$view_name<'a, A, D> as IntoIterator>::Item;
184        fn split(self) -> (Self, Option<Self>) {
185            if self.0.len() <= self.1 {
186                return (self, None)
187            }
188            let array = self.0;
189            let max_axis = array.max_stride_axis();
190            let mid = array.len_of(max_axis) / 2;
191            let (a, b) = array.split_at(max_axis, mid);
192            (ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1)))
193        }
194
195        fn fold_with<F>(self, folder: F) -> F
196            where F: Folder<Self::Item>,
197        {
198            Zip::from(self.0).fold_while(folder, |mut folder, elt| {
199                folder = folder.consume(elt);
200                if folder.full() {
201                    FoldWhile::Done(folder)
202                } else {
203                    FoldWhile::Continue(folder)
204                }
205            }).into_inner()
206        }
207    }
208
209    impl<'a, A, D> IntoIterator for ParallelProducer<$view_name<'a, A, D>>
210        where D: Dimension,
211              A: $($thread_bounds)*,
212    {
213        type Item = <$view_name<'a, A, D> as IntoIterator>::Item;
214        type IntoIter = <$view_name<'a, A, D> as IntoIterator>::IntoIter;
215        fn into_iter(self) -> Self::IntoIter {
216            self.0.into_iter()
217        }
218    }
219
220    };
221}
222
223par_iter_view_wrapper!(ArrayView, [Sync]);
224par_iter_view_wrapper!(ArrayViewMut, [Sync + Send]);
225
226use crate::{FoldWhile, NdProducer, Zip};
227
228macro_rules! zip_impl {
229    ($([$($p:ident)*],)+) => {
230        $(
231        /// Requires crate feature `rayon`.
232        #[allow(non_snake_case)]
233        impl<D, $($p),*> IntoParallelIterator for Zip<($($p,)*), D>
234            where $($p::Item : Send , )*
235                  $($p : Send , )*
236                  D: Dimension,
237                  $($p: NdProducer<Dim=D> ,)*
238        {
239            type Item = ($($p::Item ,)*);
240            type Iter = Parallel<Self>;
241            fn into_par_iter(self) -> Self::Iter {
242                Parallel {
243                    iter: self,
244                    min_len: DEFAULT_MIN_LEN,
245                }
246            }
247        }
248
249        #[allow(non_snake_case)]
250        impl<D, $($p),*> ParallelIterator for Parallel<Zip<($($p,)*), D>>
251            where $($p::Item : Send , )*
252                  $($p : Send , )*
253                  D: Dimension,
254                  $($p: NdProducer<Dim=D> ,)*
255        {
256            type Item = ($($p::Item ,)*);
257
258            fn drive_unindexed<Cons>(self, consumer: Cons) -> Cons::Result
259                where Cons: UnindexedConsumer<Self::Item>
260            {
261                bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer)
262            }
263
264            fn opt_len(&self) -> Option<usize> {
265                None
266            }
267        }
268
269        #[allow(non_snake_case)]
270        impl<D, $($p),*> UnindexedProducer for ParallelProducer<Zip<($($p,)*), D>>
271            where $($p : Send , )*
272                  $($p::Item : Send , )*
273                  D: Dimension,
274                  $($p: NdProducer<Dim=D> ,)*
275        {
276            type Item = ($($p::Item ,)*);
277
278            fn split(self) -> (Self, Option<Self>) {
279                if self.0.size() <= self.1 {
280                    return (self, None)
281                }
282                let (a, b) = self.0.split();
283                (ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1)))
284            }
285
286            fn fold_with<Fold>(self, folder: Fold) -> Fold
287                where Fold: Folder<Self::Item>,
288            {
289                self.0.fold_while(folder, |mut folder, $($p),*| {
290                    folder = folder.consume(($($p ,)*));
291                    if folder.full() {
292                        FoldWhile::Done(folder)
293                    } else {
294                        FoldWhile::Continue(folder)
295                    }
296                }).into_inner()
297            }
298        }
299        )+
300    };
301}
302
303zip_impl! {
304    [P1],
305    [P1 P2],
306    [P1 P2 P3],
307    [P1 P2 P3 P4],
308    [P1 P2 P3 P4 P5],
309    [P1 P2 P3 P4 P5 P6],
310}
311
312impl<D, Parts> Parallel<Zip<Parts, D>>
313where D: Dimension
314{
315    /// Sets the minimum number of elements desired to process in each job. This will not be
316    /// split any smaller than this length, but of course a producer could already be smaller
317    /// to begin with.
318    ///
319    /// ***Panics*** if `min_len` is zero.
320    pub fn with_min_len(self, min_len: usize) -> Self
321    {
322        assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks.");
323
324        Self { min_len, ..self }
325    }
326}
327
328/// A parallel iterator (unindexed) that produces the splits of the array
329/// or producer `P`.
330pub(crate) struct ParallelSplits<P>
331{
332    pub(crate) iter: P,
333    pub(crate) max_splits: usize,
334}
335
336impl<P> ParallelIterator for ParallelSplits<P>
337where P: SplitPreference + Send
338{
339    type Item = P;
340
341    fn drive_unindexed<C>(self, consumer: C) -> C::Result
342    where C: UnindexedConsumer<Self::Item>
343    {
344        bridge_unindexed(self, consumer)
345    }
346
347    fn opt_len(&self) -> Option<usize>
348    {
349        None
350    }
351}
352
353impl<P> UnindexedProducer for ParallelSplits<P>
354where P: SplitPreference + Send
355{
356    type Item = P;
357
358    fn split(self) -> (Self, Option<Self>)
359    {
360        if self.max_splits == 0 || !self.iter.can_split() {
361            return (self, None);
362        }
363        let (a, b) = self.iter.split();
364        (
365            ParallelSplits {
366                iter: a,
367                max_splits: self.max_splits - 1,
368            },
369            Some(ParallelSplits {
370                iter: b,
371                max_splits: self.max_splits - 1,
372            }),
373        )
374    }
375
376    fn fold_with<Fold>(self, folder: Fold) -> Fold
377    where Fold: Folder<Self::Item>
378    {
379        folder.consume(self.iter)
380    }
381}