Skip to content

Commit b4871e8

Browse files
authored
Merge pull request #288 from bluss/zip-parallel
Rayon parallelization for Zip / azip!
2 parents bcf3971 + d33eddc commit b4871e8

File tree

10 files changed

+378
-28
lines changed

10 files changed

+378
-28
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ script:
3131
cargo test --release --verbose --no-default-features &&
3232
cargo build --verbose --features "$FEATURES" &&
3333
cargo test --verbose --features "$FEATURES" &&
34+
CARGO_TARGET_DIR=target/ cargo test --manifest-path=parallel/Cargo.toml --verbose &&
3435
CARGO_TARGET_DIR=target/ cargo test --manifest-path=serialization-tests/Cargo.toml --verbose &&
3536
CARGO_TARGET_DIR=target/ cargo test --manifest-path=numeric-tests/Cargo.toml --verbose &&
36-
CARGO_TARGET_DIR=target/ cargo test --manifest-path=parallel/Cargo.toml --verbose &&
3737
([ "$BENCH" != 1 ] || cargo bench --no-run --verbose --features "$FEATURES")

parallel/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ keywords = ["data-structure", "multidimensional", "parallel", "concurrent"]
1313
categories = ["data-structures", "science", "concurrency"]
1414

1515
[dependencies]
16-
rayon = "0.6"
16+
rayon = { version = "0.7.0" }
1717
ndarray = { version = "0.9.0-alpha.1", path = "../" }
1818

1919
[dev-dependencies]

parallel/README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ How to use with cargo::
4747
Recent Changes (ndarray-parallel)
4848
---------------------------------
4949

50+
- 0.3.0-alpha.1
51+
52+
- ParallelIterator for Zip, including ``.par_apply``.
53+
- ``.par_map_inplace`` and ``.par_mav_inplace`` for arrays
54+
- Require ndarray 0.9 (when released) and rayon 0.7
55+
5056
- 0.2.0
5157

5258
- Require for ndarray 0.8

parallel/benches/rayon.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ extern crate ndarray_parallel;
1212
use ndarray::prelude::*;
1313
use ndarray_parallel::prelude::*;
1414

15+
use ndarray::Zip;
16+
1517
const EXP_N: usize = 128;
18+
const ADDN: usize = 1024;
1619

1720
use std::cmp::max;
1821

1922
fn set_threads() {
2023
let n = max(1, num_cpus::get() / 2);
21-
let cfg = rayon::Configuration::new().set_num_threads(n);
24+
//println!("Using {} threads", n);
25+
let cfg = rayon::Configuration::new().num_threads(n);
2226
let _ = rayon::initialize(cfg);
2327
}
2428

@@ -112,3 +116,42 @@ fn rayon_fastexp_by_axis(bench: &mut Bencher)
112116
.for_each(|mut sheet| sheet.mapv_inplace(fastexp));
113117
});
114118
}
119+
120+
#[bench]
121+
fn rayon_fastexp_zip(bench: &mut Bencher)
122+
{
123+
set_threads();
124+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
125+
bench.iter(|| {
126+
Zip::from(&mut a).into_par_iter().for_each(|(elt, )| *elt = fastexp(*elt));
127+
});
128+
}
129+
130+
#[bench]
131+
fn add(bench: &mut Bencher)
132+
{
133+
let mut a = Array2::<f64>::zeros((ADDN, ADDN));
134+
let b = Array2::<f64>::zeros((ADDN, ADDN));
135+
let c = Array2::<f64>::zeros((ADDN, ADDN));
136+
let d = Array2::<f64>::zeros((ADDN, ADDN));
137+
bench.iter(|| {
138+
Zip::from(&mut a).and(&b).and(&c).and(&d).apply(|a, &b, &c, &d| {
139+
*a += b.exp() + c.exp() + d.exp();
140+
})
141+
});
142+
}
143+
144+
#[bench]
145+
fn rayon_add(bench: &mut Bencher)
146+
{
147+
set_threads();
148+
let mut a = Array2::<f64>::zeros((ADDN, ADDN));
149+
let b = Array2::<f64>::zeros((ADDN, ADDN));
150+
let c = Array2::<f64>::zeros((ADDN, ADDN));
151+
let d = Array2::<f64>::zeros((ADDN, ADDN));
152+
bench.iter(|| {
153+
Zip::from(&mut a).and(&b).and(&c).and(&d).into_par_iter().for_each(|(a, &b, &c, &d)| {
154+
*a += b.exp() + c.exp() + d.exp();
155+
})
156+
});
157+
}

parallel/src/ext_traits.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
2+
use ndarray::{
3+
Dimension,
4+
NdProducer,
5+
Zip,
6+
ArrayBase,
7+
DataMut,
8+
};
9+
10+
use prelude::*;
11+
12+
// Arrays
13+
14+
/// Parallel versions of `map_inplace` and `mapv_inplace`.
15+
pub trait ParMap {
16+
type Item;
17+
fn par_map_inplace<F>(&mut self, f: F)
18+
where F: Fn(&mut Self::Item) + Sync;
19+
fn par_mapv_inplace<F>(&mut self, f: F)
20+
where F: Fn(Self::Item) -> Self::Item + Sync,
21+
Self::Item: Clone;
22+
}
23+
24+
impl<A, S, D> ParMap for ArrayBase<S, D>
25+
where S: DataMut<Elem=A>,
26+
D: Dimension,
27+
A: Send + Sync,
28+
{
29+
type Item = A;
30+
fn par_map_inplace<F>(&mut self, f: F)
31+
where F: Fn(&mut Self::Item) + Sync
32+
{
33+
self.view_mut().into_par_iter().for_each(f)
34+
}
35+
fn par_mapv_inplace<F>(&mut self, f: F)
36+
where F: Fn(Self::Item) -> Self::Item + Sync,
37+
Self::Item: Clone
38+
{
39+
self.view_mut().into_par_iter()
40+
.for_each(move |x| *x = f(x.clone()))
41+
}
42+
}
43+
44+
45+
46+
47+
// Zip
48+
49+
macro_rules! zip_impl {
50+
($([$name:ident $($p:ident)*],)+) => {
51+
$(
52+
/// The `par_apply` method for `Zip`.
53+
///
54+
/// This is a shorthand for using `.into_par_iter().for_each()` on
55+
/// `Zip`.
56+
pub trait $name<$($p),*> {
57+
fn par_apply<F>(self, function: F)
58+
where F: Fn($($p),*) + Sync;
59+
}
60+
61+
#[allow(non_snake_case)]
62+
impl<Dim: Dimension, $($p: NdProducer<Dim=Dim>),*> $name<$($p::Item),*> for Zip<($($p,)*), Dim>
63+
where $($p::Item : Send , )*
64+
$($p : Send , )*
65+
{
66+
fn par_apply<F>(self, function: F)
67+
where F: Fn($($p::Item),*) + Sync
68+
{
69+
self.into_par_iter().for_each(move |($($p,)*)| function($($p),*))
70+
}
71+
}
72+
)+
73+
}
74+
}
75+
76+
zip_impl!{
77+
[ParApply1 P1],
78+
[ParApply2 P1 P2],
79+
[ParApply3 P1 P2 P3],
80+
[ParApply4 P1 P2 P3 P4],
81+
[ParApply5 P1 P2 P3 P4 P5],
82+
[ParApply6 P1 P2 P3 P4 P5 P6],
83+
}

parallel/src/into_traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
use rayon::par_iter::ParallelIterator;
2+
use rayon::iter::ParallelIterator;
33

44
pub trait NdarrayIntoParallelIterator {
55
type Iter: ParallelIterator<Item=Self::Item>;

parallel/src/lib.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88
//! `.axis_iter()` and `.axis_iter_mut()` also have parallel counterparts,
99
//! and their parallel iterators are indexed (and thus ordered) and exact length.
1010
//!
11+
//! `Zip` also implements `NdarrayIntoParallelIterator`, and there is an
12+
//! extension trait so that it can use a method `.par_apply` directly.
13+
//!
1114
//! (*) This regime of a custom trait instead of rayon’s own is since we
1215
//! use this intermediate ndarray-parallel crate.
1316
//!
1417
//! # Examples
1518
//!
19+
//!
20+
//! ## Arrays and array views
21+
//!
1622
//! Compute the exponential of each element in an array, parallelized.
1723
//!
1824
//! ```
@@ -24,10 +30,18 @@
2430
//!
2531
//! fn main() {
2632
//! let mut a = Array2::<f64>::zeros((128, 128));
33+
//!
34+
//! // Parallel versions of regular array methods (ParMap trait)
35+
//! a.par_map_inplace(|x| *x = x.exp());
36+
//! a.par_mapv_inplace(f64::exp);
37+
//!
38+
//! // You can also use the parallel iterator directly
2739
//! a.par_iter_mut().for_each(|x| *x = x.exp());
2840
//! }
2941
//! ```
3042
//!
43+
//! ## Axis iterators
44+
//!
3145
//! Use the parallel `.axis_iter()` to compute the sum of each row.
3246
//!
3347
//! ```
@@ -49,10 +63,39 @@
4963
//! assert_eq!(sums, [120., 376., 632., 888.]);
5064
//! }
5165
//! ```
66+
//!
67+
//! ## Zip
68+
//!
69+
//! Use zip for lock step function application across several arrays
70+
//!
71+
//! ```
72+
//! extern crate ndarray;
73+
//! extern crate ndarray_parallel;
74+
//!
75+
//! use ndarray::Array3;
76+
//! use ndarray::Zip;
77+
//! use ndarray_parallel::prelude::*;
78+
//!
79+
//! type Array3f64 = Array3<f64>;
80+
//!
81+
//! fn main() {
82+
//! const N: usize = 128;
83+
//! let a = Array3f64::from_elem((N, N, N), 1.);
84+
//! let b = Array3f64::from_elem(a.dim(), 2.);
85+
//! let mut c = Array3f64::zeros(a.dim());
86+
//!
87+
//! Zip::from(&mut c)
88+
//! .and(&a)
89+
//! .and(&b)
90+
//! .par_apply(|c, &a, &b| {
91+
//! *c += a - b;
92+
//! });
93+
//! }
94+
//! ```
5295
5396

54-
extern crate ndarray;
55-
extern crate rayon;
97+
pub extern crate ndarray;
98+
pub extern crate rayon;
5699

57100
/// Into- traits for creating parallelized iterators.
58101
pub mod prelude {
@@ -63,6 +106,16 @@ pub mod prelude {
63106

64107
#[doc(no_inline)]
65108
pub use rayon::prelude::{ParallelIterator, IndexedParallelIterator, ExactParallelIterator};
109+
110+
pub use ext_traits::{
111+
ParApply1,
112+
ParApply2,
113+
ParApply3,
114+
ParApply4,
115+
ParApply5,
116+
ParApply6,
117+
};
118+
pub use ext_traits::ParMap;
66119
}
67120

68121
pub use par::Parallel;
@@ -73,5 +126,6 @@ pub use into_traits::{
73126
};
74127

75128
mod par;
129+
mod ext_traits;
76130
mod into_traits;
77131
mod into_impls;

0 commit comments

Comments
 (0)