Skip to main content

rten_tensor/iterators/
parallel.rs

1use rayon::prelude::*;
2use rten_base::iter::SplitIterator;
3use rten_parallel::par_iter::ParIter;
4
5use super::{
6    AxisChunks, AxisChunksMut, AxisIter, AxisIterMut, InnerIter, InnerIterBase, InnerIterMut, Iter,
7    IterMut, LaneRanges, Lanes, LanesMut, Offsets, OffsetsKind,
8};
9use crate::Storage;
10use crate::layout::{Layout, MutLayout, RemoveDim};
11
12/// Generate the body of an [`IntoParallelIterator`] impl which uses [`ParIter`]
13/// as the iterator type.
14macro_rules! impl_parallel_iterator {
15    () => {
16        type Iter = ParIter<Self>;
17        type Item = <Self as Iterator>::Item;
18
19        fn into_par_iter(self) -> Self::Iter {
20            self.into()
21        }
22    };
23}
24
25impl SplitIterator for Offsets {
26    fn split_at(self, index: usize) -> (Self, Self) {
27        assert!(index <= self.len());
28        let (left_kind, right_kind) = match self.base {
29            OffsetsKind::Range(r) => {
30                let left = r.start..r.start + index;
31                let right = r.start + index..r.end;
32                (OffsetsKind::Range(left), OffsetsKind::Range(right))
33            }
34            OffsetsKind::Indexing(base) => {
35                let (left, right) = base.split_at(index);
36                (OffsetsKind::Indexing(left), OffsetsKind::Indexing(right))
37            }
38        };
39        (Offsets { base: left_kind }, Offsets { base: right_kind })
40    }
41}
42
43impl<L: Layout + Clone> SplitIterator for InnerIterBase<L> {
44    fn split_at(self, index: usize) -> (Self, Self) {
45        let (left_offsets, right_offsets) = self.outer_offsets.split_at(index);
46        let left = Self {
47            outer_offsets: left_offsets,
48            inner_layout: self.inner_layout.clone(),
49            inner_data_len: self.inner_data_len,
50        };
51        let right = Self {
52            outer_offsets: right_offsets,
53            inner_layout: self.inner_layout,
54            inner_data_len: self.inner_data_len,
55        };
56        (left, right)
57    }
58}
59
60impl<'a, T, L: MutLayout + Send + Sync> SplitIterator for InnerIter<'a, T, L> {
61    fn split_at(self, index: usize) -> (Self, Self) {
62        let (left_base, right_base) = self.base.split_at(index);
63        let left = Self {
64            base: left_base,
65            data: self.data,
66        };
67        let right = Self {
68            base: right_base,
69            data: self.data,
70        };
71        (left, right)
72    }
73}
74
75impl<'a, T, L: MutLayout + Send + Sync> IntoParallelIterator for InnerIter<'a, T, L> {
76    impl_parallel_iterator!();
77}
78
79impl<'a, T, L: MutLayout + Send + Sync> SplitIterator for InnerIterMut<'a, T, L> {
80    fn split_at(self, index: usize) -> (Self, Self) {
81        let (left_base, right_base) = self.base.split_at(index);
82        let len = self.data.len();
83
84        // The left/right splits use the same storage. We rely on the left/right
85        // layouts being logically disjoint to ensure we don't create multiple
86        // mutable references to the same elements.
87        let (left_data, right_data) = self.data.split_mut(0..len, 0..len);
88
89        let left = Self {
90            base: left_base,
91            data: left_data,
92        };
93        let right = Self {
94            base: right_base,
95            data: right_data,
96        };
97        (left, right)
98    }
99}
100
101impl<'a, T, L: MutLayout + Send + Sync> IntoParallelIterator for InnerIterMut<'a, T, L> {
102    impl_parallel_iterator!();
103}
104
105impl<'a, T, L: MutLayout + RemoveDim> SplitIterator for AxisIter<'a, T, L> {
106    fn split_at(self, index: usize) -> (Self, Self) {
107        let (left_view, right_view) = self.view.split_at(self.axis, index);
108        let left = AxisIter::new(&left_view, self.axis);
109        let right = AxisIter::new(&right_view, self.axis);
110        (left, right)
111    }
112}
113
114impl<'a, T, L: MutLayout + RemoveDim + Send> IntoParallelIterator for AxisIter<'a, T, L>
115where
116    <L as RemoveDim>::Output: Send,
117{
118    impl_parallel_iterator!();
119}
120
121impl<'a, T, L: MutLayout + RemoveDim> SplitIterator for AxisIterMut<'a, T, L> {
122    fn split_at(self, index: usize) -> (Self, Self) {
123        let (left_view, right_view) = self.view.split_at_mut(self.axis, index);
124        let left = AxisIterMut::new(left_view, self.axis);
125        let right = AxisIterMut::new(right_view, self.axis);
126        (left, right)
127    }
128}
129
130impl<'a, T, L: MutLayout + RemoveDim + Send> IntoParallelIterator for AxisIterMut<'a, T, L>
131where
132    <L as RemoveDim>::Output: Send,
133{
134    impl_parallel_iterator!();
135}
136
137impl<'a, T, L: MutLayout> SplitIterator for AxisChunks<'a, T, L> {
138    fn split_at(mut self, index: usize) -> (Self, Self) {
139        let (left_remainder, right_remainder) = if let Some(remainder) = self.remainder.take() {
140            let (l, r) = remainder.split_at(self.axis, self.chunk_size * index);
141            (Some(l), Some(r))
142        } else {
143            (None, None)
144        };
145
146        let left = AxisChunks {
147            remainder: left_remainder,
148            axis: self.axis,
149            chunk_size: self.chunk_size,
150        };
151        let right = AxisChunks {
152            remainder: right_remainder,
153            axis: self.axis,
154            chunk_size: self.chunk_size,
155        };
156
157        (left, right)
158    }
159}
160
161impl<'a, T, L: MutLayout + Send> IntoParallelIterator for AxisChunks<'a, T, L> {
162    impl_parallel_iterator!();
163}
164
165impl<'a, T, L: MutLayout> SplitIterator for AxisChunksMut<'a, T, L> {
166    fn split_at(mut self, index: usize) -> (Self, Self) {
167        let (left_remainder, right_remainder) = if let Some(remainder) = self.remainder.take() {
168            let (l, r) = remainder.split_at_mut(self.axis, self.chunk_size * index);
169            (Some(l), Some(r))
170        } else {
171            (None, None)
172        };
173
174        let left = Self {
175            remainder: left_remainder,
176            axis: self.axis,
177            chunk_size: self.chunk_size,
178        };
179        let right = Self {
180            remainder: right_remainder,
181            axis: self.axis,
182            chunk_size: self.chunk_size,
183        };
184
185        (left, right)
186    }
187}
188
189impl<'a, T, L: MutLayout + Send> IntoParallelIterator for AxisChunksMut<'a, T, L> {
190    impl_parallel_iterator!();
191}
192
193impl<'a, T> SplitIterator for Iter<'a, T> {
194    fn split_at(self, index: usize) -> (Self, Self) {
195        let (left_offsets, right_offsets) = self.offsets.split_at(index);
196        let left = Self {
197            offsets: left_offsets,
198            data: self.data,
199        };
200        let right = Self {
201            offsets: right_offsets,
202            data: self.data,
203        };
204        (left, right)
205    }
206}
207
208impl<'a, T: Sync> IntoParallelIterator for Iter<'a, T> {
209    impl_parallel_iterator!();
210}
211
212impl<'a, T> SplitIterator for IterMut<'a, T> {
213    fn split_at(self, index: usize) -> (Self, Self) {
214        let (left_offsets, right_offsets) = self.offsets.split_at(index);
215        let len = self.data.len();
216        let (left_data, right_data) = self.data.split_mut(0..len, 0..len);
217        let left = Self {
218            offsets: left_offsets,
219            data: left_data,
220        };
221        let right = Self {
222            offsets: right_offsets,
223            data: right_data,
224        };
225        (left, right)
226    }
227}
228
229impl<'a, T: Sync + Send> IntoParallelIterator for IterMut<'a, T> {
230    impl_parallel_iterator!();
231}
232
233impl SplitIterator for LaneRanges {
234    fn split_at(self, index: usize) -> (Self, Self) {
235        let (left_offsets, right_offsets) = self.offsets.split_at(index);
236        let left = LaneRanges {
237            offsets: left_offsets,
238            dim_size: self.dim_size,
239            dim_stride: self.dim_stride,
240        };
241        let right = LaneRanges {
242            offsets: right_offsets,
243            dim_size: self.dim_size,
244            dim_stride: self.dim_stride,
245        };
246        (left, right)
247    }
248}
249
250impl<'a, T> SplitIterator for Lanes<'a, T> {
251    fn split_at(self, index: usize) -> (Self, Self) {
252        let (left_range, right_range) = self.ranges.split_at(index);
253
254        let left = Lanes {
255            data: self.data,
256            ranges: left_range,
257            lane_layout: self.lane_layout,
258        };
259        let right = Lanes {
260            data: self.data,
261            ranges: right_range,
262            lane_layout: self.lane_layout,
263        };
264
265        (left, right)
266    }
267}
268
269impl<'a, T: Sync + Send> IntoParallelIterator for Lanes<'a, T> {
270    impl_parallel_iterator!();
271}
272
273impl<'a, T> SplitIterator for LanesMut<'a, T> {
274    fn split_at(self, index: usize) -> (Self, Self) {
275        let (left_range, right_range) = self.ranges.split_at(index);
276        let len = self.data.len();
277
278        // Safety note: `split_mut` relies on the caller to ensure that
279        // associated layouts do not overlap.
280        let (left_data, right_data) = self.data.split_mut(0..len, 0..len);
281
282        let left = Self {
283            data: left_data,
284            ranges: left_range,
285            lane_layout: self.lane_layout,
286        };
287        let right = Self {
288            data: right_data,
289            ranges: right_range,
290            lane_layout: self.lane_layout,
291        };
292
293        (left, right)
294    }
295}
296
297impl<T: Sync + Send> IntoParallelIterator for LanesMut<'_, T> {
298    impl_parallel_iterator!();
299}
300
301#[cfg(test)]
302mod tests {
303    use rayon::prelude::*;
304
305    use crate::rng::XorShiftRng;
306    use crate::{AsView, Tensor};
307
308    // These helpers use macros to work around difficulties expressing lifetime
309    // relationships between input and output in closures that take an `&Tensor`
310    // and return an `impl Iterator + IntoParallelIterator`.
311
312    // Test that the parallel version of an iterator yields the same items as
313    // the serial version.
314    macro_rules! test_parallel_iterator {
315        ($x:ident, $iter:expr) => {
316            let mut rng = XorShiftRng::new(1234);
317            let $x = Tensor::<f32>::rand(&[4, 8, 16, 32], &mut rng);
318            let serial: Vec<_> = $iter.collect();
319            let parallel: Vec<_> = $iter.into_par_iter().collect();
320            assert_eq!(serial, parallel);
321        };
322    }
323
324    // Test that the parallel version of a mutable iterator yields the same
325    // items as the serial version.
326    macro_rules! test_parallel_iterator_mut {
327        ($x:ident, $iter:expr, $item_sum:expr) => {
328            let mut rng = XorShiftRng::new(1234);
329
330            // Use ints rather than floats here to avoid mismatches due to
331            // parallel iteration visiting items in a different order to serial
332            // iteration.
333            let mut $x =
334                Tensor::<i32>::from_simple_fn(&[4, 8, 16, 32], || (rng.next_f32() * 100.) as i32);
335            let serial: i32 = $iter.map($item_sum).sum();
336            let parallel: i32 = $iter.into_par_iter().map($item_sum).sum();
337
338            assert_eq!(serial, parallel);
339        };
340    }
341
342    // Test that the parallel version of an iterator yields the same items as
343    // the serial version.
344    //
345    // This is a variant for the case where the items are themselves iterators.
346    macro_rules! test_parallel_iterator_flatten {
347        ($x:ident, $iter:expr) => {
348            let mut rng = XorShiftRng::new(1234);
349            let $x = Tensor::<f32>::rand(&[4, 8, 16, 32], &mut rng);
350
351            let serial: Vec<_> = $iter.collect();
352            let parallel: Vec<_> = $iter.into_par_iter().collect();
353
354            let serial_items: Vec<f32> = serial.into_iter().flatten().copied().collect();
355            let parallel_items: Vec<f32> = parallel.into_iter().flatten().copied().collect();
356            assert_eq!(serial_items, parallel_items);
357        };
358    }
359
360    // Parallel tests are skipped under Miri due to
361    // https://github.com/crossbeam-rs/crossbeam/issues/1181.
362
363    #[test]
364    #[cfg_attr(miri, ignore)]
365    fn test_inner_iter_parallel() {
366        test_parallel_iterator!(x, x.inner_iter::<2>());
367    }
368
369    #[test]
370    #[cfg_attr(miri, ignore)]
371    fn test_inner_iter_mut_parallel() {
372        test_parallel_iterator_mut!(x, x.inner_iter_mut::<2>(), |x| x.iter().sum::<i32>());
373    }
374
375    #[test]
376    #[cfg_attr(miri, ignore)]
377    fn test_iter_parallel() {
378        test_parallel_iterator!(x, x.iter());
379    }
380
381    #[test]
382    #[cfg_attr(miri, ignore)]
383    fn test_iter_mut_parallel() {
384        test_parallel_iterator_mut!(x, x.iter_mut(), |x| *x);
385    }
386
387    #[test]
388    #[cfg_attr(miri, ignore)]
389    fn test_axis_chunks_parallel() {
390        test_parallel_iterator!(x, x.axis_chunks(0, 2));
391    }
392
393    #[test]
394    #[cfg_attr(miri, ignore)]
395    fn test_axis_chunks_mut_parallel() {
396        test_parallel_iterator_mut!(x, x.axis_chunks_mut(0, 2), |x| x.iter().sum::<i32>());
397    }
398
399    #[test]
400    #[cfg_attr(miri, ignore)]
401    fn test_axis_iter_parallel() {
402        test_parallel_iterator!(x, x.axis_iter(0));
403    }
404
405    #[test]
406    #[cfg_attr(miri, ignore)]
407    fn test_axis_iter_mut_parallel() {
408        test_parallel_iterator_mut!(x, x.axis_iter_mut(0), |x| x.iter().sum::<i32>());
409    }
410
411    #[test]
412    #[cfg_attr(miri, ignore)]
413    fn test_lanes_parallel() {
414        test_parallel_iterator_flatten!(x, x.lanes(0));
415    }
416
417    #[test]
418    #[cfg_attr(miri, ignore)]
419    fn test_lanes_mut_parallel() {
420        test_parallel_iterator_mut!(x, x.lanes_mut(0), |x| x.map(|x| *x).sum::<i32>());
421    }
422}