rayon/iter/multizip.rs
1use super::plumbing::*;
2use super::*;
3
4/// `MultiZip` is an iterator that zips up a tuple of parallel iterators to
5/// produce tuples of their items.
6///
7/// It is created by calling `into_par_iter()` on a tuple of types that
8/// implement `IntoParallelIterator`, or `par_iter()`/`par_iter_mut()` with
9/// types that are iterable by reference.
10///
11/// The implementation currently support tuples up to length 12.
12///
13/// # Examples
14///
15/// ```
16/// use rayon::prelude::*;
17///
18/// // This will iterate `r` by mutable reference, like `par_iter_mut()`, while
19/// // ranges are all iterated by value like `into_par_iter()`.
20/// // Note that the zipped iterator is only as long as the shortest input.
21/// let mut r = vec![0; 3];
22/// (&mut r, 1..10, 10..100, 100..1000).into_par_iter()
23/// .for_each(|(r, x, y, z)| *r = x * y + z);
24///
25/// assert_eq!(&r, &[1 * 10 + 100, 2 * 11 + 101, 3 * 12 + 102]);
26/// ```
27///
28/// For a group that should all be iterated by reference, you can use a tuple reference.
29///
30/// ```
31/// use rayon::prelude::*;
32///
33/// let xs: Vec<_> = (1..10).collect();
34/// let ys: Vec<_> = (10..100).collect();
35/// let zs: Vec<_> = (100..1000).collect();
36///
37/// // Reference each input separately with `IntoParallelIterator`:
38/// let r1: Vec<_> = (&xs, &ys, &zs).into_par_iter()
39/// .map(|(x, y, z)| x * y + z)
40/// .collect();
41///
42/// // Reference them all together with `IntoParallelRefIterator`:
43/// let r2: Vec<_> = (xs, ys, zs).par_iter()
44/// .map(|(x, y, z)| x * y + z)
45/// .collect();
46///
47/// assert_eq!(r1, r2);
48/// ```
49///
50/// Mutable references to a tuple will work similarly.
51///
52/// ```
53/// use rayon::prelude::*;
54///
55/// let mut xs: Vec<_> = (1..4).collect();
56/// let mut ys: Vec<_> = (-4..-1).collect();
57/// let mut zs = vec![0; 3];
58///
59/// // Mutably reference each input separately with `IntoParallelIterator`:
60/// (&mut xs, &mut ys, &mut zs).into_par_iter().for_each(|(x, y, z)| {
61/// *z += *x + *y;
62/// std::mem::swap(x, y);
63/// });
64///
65/// assert_eq!(xs, (vec![-4, -3, -2]));
66/// assert_eq!(ys, (vec![1, 2, 3]));
67/// assert_eq!(zs, (vec![-3, -1, 1]));
68///
69/// // Mutably reference them all together with `IntoParallelRefMutIterator`:
70/// let mut tuple = (xs, ys, zs);
71/// tuple.par_iter_mut().for_each(|(x, y, z)| {
72/// *z += *x + *y;
73/// std::mem::swap(x, y);
74/// });
75///
76/// assert_eq!(tuple, (vec![1, 2, 3], vec![-4, -3, -2], vec![-6, -2, 2]));
77/// ```
78#[derive(Debug, Clone)]
79pub struct MultiZip<T> {
80 tuple: T,
81}
82
83// These macros greedily consume 4 or 2 items first to achieve log2 nesting depth.
84// For example, 5 => 4,1 => (2,2),1.
85//
86// The tuples go up to 12, so we might want to greedily consume 8 too, but
87// the depth works out the same if we let that expand on the right:
88// 9 => 4,5 => (2,2),(4,1) => (2,2),((2,2),1)
89// 12 => 4,8 => (2,2),(4,4) => (2,2),((2,2),(2,2))
90//
91// But if we ever increase to 13, we would want to split 8,5 rather than 4,9.
92
93macro_rules! reduce {
94 ($a:expr, $b:expr, $c:expr, $d:expr, $( $x:expr ),+ => $fn:path) => {
95 reduce!(reduce!($a, $b, $c, $d => $fn),
96 reduce!($( $x ),+ => $fn)
97 => $fn)
98 };
99 ($a:expr, $b:expr, $( $x:expr ),+ => $fn:path) => {
100 reduce!(reduce!($a, $b => $fn),
101 reduce!($( $x ),+ => $fn)
102 => $fn)
103 };
104 ($a:expr, $b:expr => $fn:path) => { $fn($a, $b) };
105 ($a:expr => $fn:path) => { $a };
106}
107
108macro_rules! nest {
109 ($A:tt, $B:tt, $C:tt, $D:tt, $( $X:tt ),+) => {
110 (nest!($A, $B, $C, $D), nest!($( $X ),+))
111 };
112 ($A:tt, $B:tt, $( $X:tt ),+) => {
113 (($A, $B), nest!($( $X ),+))
114 };
115 ($A:tt, $B:tt) => { ($A, $B) };
116 ($A:tt) => { $A };
117}
118
119macro_rules! flatten {
120 ($( $T:ident ),+) => {{
121 #[allow(non_snake_case)]
122 fn flatten<$( $T ),+>(nest!($( $T ),+) : nest!($( $T ),+)) -> ($( $T, )+) {
123 ($( $T, )+)
124 }
125 flatten
126 }};
127}
128
129macro_rules! multizip_impls {
130 ($(
131 $Tuple:ident {
132 $(($idx:tt) -> $T:ident)+
133 }
134 )+) => {
135 $(
136 impl<$( $T, )+> IntoParallelIterator for ($( $T, )+)
137 where
138 $(
139 $T: IntoParallelIterator<Iter: IndexedParallelIterator>,
140 )+
141 {
142 type Item = ($( $T::Item, )+);
143 type Iter = MultiZip<($( $T::Iter, )+)>;
144
145 fn into_par_iter(self) -> Self::Iter {
146 MultiZip {
147 tuple: ( $( self.$idx.into_par_iter(), )+ ),
148 }
149 }
150 }
151
152 impl<'a, $( $T, )+> IntoParallelIterator for &'a ($( $T, )+)
153 where
154 $(
155 $T: IntoParallelRefIterator<'a, Iter: IndexedParallelIterator>,
156 )+
157 {
158 type Item = ($( $T::Item, )+);
159 type Iter = MultiZip<($( $T::Iter, )+)>;
160
161 fn into_par_iter(self) -> Self::Iter {
162 MultiZip {
163 tuple: ( $( self.$idx.par_iter(), )+ ),
164 }
165 }
166 }
167
168 impl<'a, $( $T, )+> IntoParallelIterator for &'a mut ($( $T, )+)
169 where
170 $(
171 $T: IntoParallelRefMutIterator<'a, Iter: IndexedParallelIterator>,
172 )+
173 {
174 type Item = ($( $T::Item, )+);
175 type Iter = MultiZip<($( $T::Iter, )+)>;
176
177 fn into_par_iter(self) -> Self::Iter {
178 MultiZip {
179 tuple: ( $( self.$idx.par_iter_mut(), )+ ),
180 }
181 }
182 }
183
184 impl<$( $T, )+> ParallelIterator for MultiZip<($( $T, )+)>
185 where
186 $( $T: IndexedParallelIterator, )+
187 {
188 type Item = ($( $T::Item, )+);
189
190 fn drive_unindexed<CONSUMER>(self, consumer: CONSUMER) -> CONSUMER::Result
191 where
192 CONSUMER: UnindexedConsumer<Self::Item>,
193 {
194 self.drive(consumer)
195 }
196
197 fn opt_len(&self) -> Option<usize> {
198 Some(self.len())
199 }
200 }
201
202 impl<$( $T, )+> IndexedParallelIterator for MultiZip<($( $T, )+)>
203 where
204 $( $T: IndexedParallelIterator, )+
205 {
206 fn drive<CONSUMER>(self, consumer: CONSUMER) -> CONSUMER::Result
207 where
208 CONSUMER: Consumer<Self::Item>,
209 {
210 reduce!($( self.tuple.$idx ),+ => IndexedParallelIterator::zip)
211 .map(flatten!($( $T ),+))
212 .drive(consumer)
213 }
214
215 fn len(&self) -> usize {
216 reduce!($( self.tuple.$idx.len() ),+ => Ord::min)
217 }
218
219 fn with_producer<CB>(self, callback: CB) -> CB::Output
220 where
221 CB: ProducerCallback<Self::Item>,
222 {
223 reduce!($( self.tuple.$idx ),+ => IndexedParallelIterator::zip)
224 .map(flatten!($( $T ),+))
225 .with_producer(callback)
226 }
227 }
228 )+
229 }
230}
231
232multizip_impls! {
233 Tuple1 {
234 (0) -> A
235 }
236 Tuple2 {
237 (0) -> A
238 (1) -> B
239 }
240 Tuple3 {
241 (0) -> A
242 (1) -> B
243 (2) -> C
244 }
245 Tuple4 {
246 (0) -> A
247 (1) -> B
248 (2) -> C
249 (3) -> D
250 }
251 Tuple5 {
252 (0) -> A
253 (1) -> B
254 (2) -> C
255 (3) -> D
256 (4) -> E
257 }
258 Tuple6 {
259 (0) -> A
260 (1) -> B
261 (2) -> C
262 (3) -> D
263 (4) -> E
264 (5) -> F
265 }
266 Tuple7 {
267 (0) -> A
268 (1) -> B
269 (2) -> C
270 (3) -> D
271 (4) -> E
272 (5) -> F
273 (6) -> G
274 }
275 Tuple8 {
276 (0) -> A
277 (1) -> B
278 (2) -> C
279 (3) -> D
280 (4) -> E
281 (5) -> F
282 (6) -> G
283 (7) -> H
284 }
285 Tuple9 {
286 (0) -> A
287 (1) -> B
288 (2) -> C
289 (3) -> D
290 (4) -> E
291 (5) -> F
292 (6) -> G
293 (7) -> H
294 (8) -> I
295 }
296 Tuple10 {
297 (0) -> A
298 (1) -> B
299 (2) -> C
300 (3) -> D
301 (4) -> E
302 (5) -> F
303 (6) -> G
304 (7) -> H
305 (8) -> I
306 (9) -> J
307 }
308 Tuple11 {
309 (0) -> A
310 (1) -> B
311 (2) -> C
312 (3) -> D
313 (4) -> E
314 (5) -> F
315 (6) -> G
316 (7) -> H
317 (8) -> I
318 (9) -> J
319 (10) -> K
320 }
321 Tuple12 {
322 (0) -> A
323 (1) -> B
324 (2) -> C
325 (3) -> D
326 (4) -> E
327 (5) -> F
328 (6) -> G
329 (7) -> H
330 (8) -> I
331 (9) -> J
332 (10) -> K
333 (11) -> L
334 }
335}