-
Notifications
You must be signed in to change notification settings - Fork 506
Commit
The `join!` macro forks and joins many expressions at once. This is easier to use than deeply nesting calls to `rayon::join`.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,3 +44,150 @@ pub use rayon_core::ThreadPool; | |
pub use rayon_core::join; | ||
pub use rayon_core::{scope, Scope}; | ||
pub use rayon_core::spawn; | ||
|
||
/// Fork and join many expressions at once. | ||
/// | ||
/// The syntax is one or more occurrences of | ||
/// | ||
/// ```ignore | ||
/// let <irrefutable pattern> = fork <closure expresssion>;`. | ||
/// ``` | ||
/// | ||
/// For example, | ||
/// | ||
/// ``` | ||
/// #[macro_use] | ||
/// extern crate rayon; | ||
/// | ||
/// # fn main() { | ||
/// join! { | ||
/// let w = fork || 0; | ||
/// let x = fork || 1; | ||
/// let y = fork || 2; | ||
/// let z = fork || 3; | ||
/// } | ||
/// | ||
/// assert_eq!(w, 0); | ||
/// assert_eq!(x, 1); | ||
/// assert_eq!(y, 2); | ||
/// assert_eq!(z, 3); | ||
/// # } | ||
/// ``` | ||
/// | ||
/// This is equivalent to nesting calls to `rayon::join` like this: | ||
/// | ||
/// ``` | ||
/// # extern crate rayon; | ||
/// let (w, (x, (y, z))) = rayon::join( | ||
/// || 0, | ||
/// || rayon::join( | ||
/// || 1, | ||
/// || rayon::join( | ||
/// || 2, | ||
/// || 3, | ||
/// ) | ||
/// ) | ||
/// ); | ||
/// ``` | ||
/// | ||
/// Alternatively, you can just get a flattened tuple of results, without | ||
/// binding the results to any variable inside the macro. | ||
/// | ||
/// The syntax is one or more occurrences of `<closure expression> ,` where the | ||
/// last `,` is optional. | ||
/// | ||
/// ```rust | ||
/// #[macro_use] | ||
/// extern crate rayon; | ||
/// | ||
/// # fn main() { | ||
/// let (w, x, y, z) = join!(|| 0, || 1, || 2, || 3); | ||
/// | ||
/// assert_eq!(w, 0); | ||
/// assert_eq!(x, 1); | ||
/// assert_eq!(y, 2); | ||
/// assert_eq!(z, 3); | ||
/// # } | ||
/// ``` | ||
#[macro_export] | ||
macro_rules! join { | ||
// Entry point for `let <pat> = fork <closure>;` usage. | ||
( $( let $lhs:pat = fork $rhs:expr ; )+ ) => { | ||
let join!( @left $( $lhs , )+ ) = join!( @right $( $rhs , )+ ); | ||
}; | ||
|
||
// Entry point for `<closure>,` usage. | ||
( $x:expr $( , $xs:expr )* ) => { | ||
join! { @flat $x $( , $xs )* } | ||
}; | ||
|
||
// Flattening tuples with temporary variables. | ||
( @flat $( let $lhs:ident = $rhs:expr ; )+ ) => { | ||
{ | ||
let join!( @left $( $lhs , )+ ) = join!( @right $( $rhs , )+ ); | ||
($( $lhs ),+) | ||
} | ||
}; | ||
( @flat $( let $lhs:ident = $rhs:expr ; )* $x:expr $( , $xs:expr )*) => { | ||
join! { @flat | ||
$( let $lhs = $rhs ; )* | ||
let lhs = $x; | ||
$($xs),* | ||
} | ||
}; | ||
|
||
// Left hand side recursion to nest individual patterns into tuple patterns | ||
// like `(x, (y, (z, ...)))`. | ||
( @left $x:pat , ) => { | ||
$x | ||
}; | ||
( @left $x:pat , $( $xs:pat , )+ ) => { | ||
( $x , join!( @left $( $xs , )+ ) ) | ||
}; | ||
|
||
// Right hand side recursion to nest exprs into rayon fork-joins | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
cuviper
Member
|
||
// like: | ||
// | ||
// rayon::join( | ||
// x, | ||
// || rayon::join( | ||
// y, | ||
// || rayon::join( | ||
// z, | ||
// || ...))) | ||
( @right $x:expr , ) => { | ||
($x)() | ||
}; | ||
( @right $x:expr , $( $xs:expr , )+ ) => { | ||
::rayon::join( $x , || join!( @right $( $xs , )+ ) ) | ||
} | ||
} | ||
|
||
// Necessary for the tests using macros that expand to `::rayon::whatever`. | ||
#[cfg(test)] | ||
mod rayon { | ||
pub use super::*; | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
#[macro_use] | ||
use super::*; | ||
|
||
#[test] | ||
fn join_macro_with_more_complex_patterns() { | ||
struct Point(usize, usize); | ||
|
||
join! { | ||
let Point(w, x) = fork || Point(1, 2); | ||
let Point(y, z) = fork || Point(3, 4); | ||
let (((((a, _), _), _), _), _) = fork || (((((5, 4), 3), 2), 1), 0); | ||
}; | ||
|
||
assert_eq!(w, 1); | ||
assert_eq!(x, 2); | ||
assert_eq!(y, 3); | ||
assert_eq!(z, 4); | ||
assert_eq!(a, 5); | ||
} | ||
} |
This recursion would divide work more efficiently if it divided the list in half and called the macro on each half. I don't know how easy/possible this is in the macro language though.