Skip to content

Commit faf347a

Browse files
committed
impl IntoParallelIterator for tuples => MultiZip
This is implemented for tuples up to arity 12, much like the standard library's trait implementations. - For `(a, b, ...)`, it calls `into_par_iter()` on each member. - For `&(a, b, ...)`, it calls `par_iter()` on each member. - For `&mut (a, b, ...)`, it calls `par_iter_mut()` on each member. The resulting `MultiZip` iterator returns a tuple of the zipped items from each input iterator. Internally, it is implemented with macros that forward to a series of regular `zip`s, mapping to a flattened tuple.
1 parent 401678e commit faf347a

File tree

4 files changed

+357
-0
lines changed

4 files changed

+357
-0
lines changed

src/iter/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ mod zip;
141141
pub use self::zip::Zip;
142142
mod zip_eq;
143143
pub use self::zip_eq::ZipEq;
144+
mod multizip;
145+
pub use self::multizip::MultiZip;
144146
mod interleave;
145147
pub use self::interleave::Interleave;
146148
mod interleave_shortest;

src/iter/multizip.rs

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

tests/clones.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,20 @@ fn clone_repeat() {
164164
fn clone_splitter() {
165165
check(rayon::iter::split(0..1000, |x| (x, None)));
166166
}
167+
168+
#[test]
169+
fn clone_multizip() {
170+
let v: &Vec<_> = &(0..1000).collect();
171+
check((v,).into_par_iter());
172+
check((v, v).into_par_iter());
173+
check((v, v, v).into_par_iter());
174+
check((v, v, v, v).into_par_iter());
175+
check((v, v, v, v, v).into_par_iter());
176+
check((v, v, v, v, v, v).into_par_iter());
177+
check((v, v, v, v, v, v, v).into_par_iter());
178+
check((v, v, v, v, v, v, v, v).into_par_iter());
179+
check((v, v, v, v, v, v, v, v, v).into_par_iter());
180+
check((v, v, v, v, v, v, v, v, v, v).into_par_iter());
181+
check((v, v, v, v, v, v, v, v, v, v, v).into_par_iter());
182+
check((v, v, v, v, v, v, v, v, v, v, v, v).into_par_iter());
183+
}

tests/debug.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,20 @@ fn debug_repeat() {
175175
fn debug_splitter() {
176176
check(rayon::iter::split(0..10, |x| (x, None)));
177177
}
178+
179+
#[test]
180+
fn debug_multizip() {
181+
let v: &Vec<_> = &(0..10).collect();
182+
check((v,).into_par_iter());
183+
check((v, v).into_par_iter());
184+
check((v, v, v).into_par_iter());
185+
check((v, v, v, v).into_par_iter());
186+
check((v, v, v, v, v).into_par_iter());
187+
check((v, v, v, v, v, v).into_par_iter());
188+
check((v, v, v, v, v, v, v).into_par_iter());
189+
check((v, v, v, v, v, v, v, v).into_par_iter());
190+
check((v, v, v, v, v, v, v, v, v).into_par_iter());
191+
check((v, v, v, v, v, v, v, v, v, v).into_par_iter());
192+
check((v, v, v, v, v, v, v, v, v, v, v).into_par_iter());
193+
check((v, v, v, v, v, v, v, v, v, v, v, v).into_par_iter());
194+
}

0 commit comments

Comments
 (0)