Skip to content

Commit 65481f5

Browse files
authored
Merge pull request #264 from bluss/parallel-crate
ndarray-parallel crate: Rayon + ndarray integration, as a crate in the land between
2 parents 288d2db + 23f596f commit 65481f5

File tree

9 files changed

+618
-0
lines changed

9 files changed

+618
-0
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ script:
3232
cargo test --release --verbose --features "" &&
3333
CARGO_TARGET_DIR=target/ cargo test --manifest-path=serialization-tests/Cargo.toml --verbose &&
3434
CARGO_TARGET_DIR=target/ cargo test --manifest-path=numeric-tests/Cargo.toml --verbose &&
35+
CARGO_TARGET_DIR=target/ cargo test --manifest-path=parallel/Cargo.toml --verbose &&
3536
([ "$BENCH" != 1 ] || cargo bench --no-run --verbose --features "$FEATURES")

parallel/Cargo.toml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "ndarray-parallel"
3+
version = "0.1.0"
4+
authors = ["bluss"]
5+
license = "MIT/Apache-2.0"
6+
7+
repository = "https://github.com/bluss/rust-ndarray"
8+
documentation = "https://docs.rs/ndarray-parallel/"
9+
10+
description = "Parallelization for ndarray (using rayon)."
11+
12+
keywords = ["data-structure", "multidimensional", "parallel", "concurrent"]
13+
14+
[dependencies]
15+
rayon = "0.6"
16+
ndarray = { version = "0.7.2", path = "../" }
17+
18+
[dev-dependencies]
19+
num_cpus = "1.2"
20+

parallel/README.rst

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
ndarray-parallel
2+
================
3+
4+
``ndarray-parallel`` integrates ndarray with rayon__ for simple parallelization.
5+
6+
__ https://github.com/nikomatsakis/rayon
7+
Please read the `API documentation here`__
8+
9+
__ http://docs.rs/ndarray-parallel/
10+
11+
|build_status|_ |crates|_
12+
13+
.. |build_status| image:: https://travis-ci.org/bluss/rust-ndarray.svg?branch=master
14+
.. _build_status: https://travis-ci.org/bluss/rust-ndarray
15+
16+
.. |crates| image:: http://meritbadge.herokuapp.com/ndarray-parallel
17+
.. _crates: https://crates.io/crates/ndarray-parallel
18+
19+
Highlights
20+
----------
21+
22+
- Parallel elementwise (no order) iterator
23+
- Parallel `.axis_iter()` (and `_mut`)
24+
25+
Status and Lookout
26+
------------------
27+
28+
- Still iterating on and evolving the crate
29+
30+
+ A separate crate is less convenient (doesn't use rayon IntoParallelIterator
31+
trait, but a separate trait) but allows rapid iteration and we can follow
32+
the evolution of rayon's internals.
33+
This crate is double pace: For every ndarray or rayon major version, this
34+
crate goes up one major version.
35+
36+
- Performance:
37+
38+
+ TBD. Tell me about your experience.
39+
+ You'll need a big chunk of data (or an expensive operation per data point)
40+
to gain from parallelization.
41+
42+
How to use with cargo::
43+
44+
[dependencies]
45+
ndarray-parallel = "0.1"
46+
47+
Recent Changes (ndarray-parallel)
48+
---------------------------------
49+
50+
- *
51+
52+
- Not yet released
53+
54+
License
55+
=======
56+
57+
Dual-licensed to be compatible with the Rust project.
58+
59+
Licensed under the Apache License, Version 2.0
60+
http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
61+
http://opensource.org/licenses/MIT, at your
62+
option. This file may not be copied, modified, or distributed
63+
except according to those terms.
64+
65+

parallel/benches/rayon.rs

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
#![feature(test)]
3+
4+
extern crate num_cpus;
5+
extern crate test;
6+
use test::Bencher;
7+
8+
extern crate rayon;
9+
#[macro_use(s)]
10+
extern crate ndarray;
11+
extern crate ndarray_parallel;
12+
use ndarray::prelude::*;
13+
use ndarray_parallel::prelude::*;
14+
15+
const EXP_N: usize = 128;
16+
17+
use std::cmp::max;
18+
19+
fn set_threads() {
20+
let n = max(1, num_cpus::get() / 2);
21+
let cfg = rayon::Configuration::new().set_num_threads(n);
22+
let _ = rayon::initialize(cfg);
23+
}
24+
25+
#[bench]
26+
fn map_exp_regular(bench: &mut Bencher)
27+
{
28+
let mut a = Array2::<f64>::zeros((EXP_N, EXP_N));
29+
a.swap_axes(0, 1);
30+
bench.iter(|| {
31+
a.mapv_inplace(|x| x.exp());
32+
});
33+
}
34+
35+
#[bench]
36+
fn rayon_exp_regular(bench: &mut Bencher)
37+
{
38+
set_threads();
39+
let mut a = Array2::<f64>::zeros((EXP_N, EXP_N));
40+
a.swap_axes(0, 1);
41+
bench.iter(|| {
42+
a.view_mut().into_par_iter().for_each(|x| *x = x.exp());
43+
});
44+
}
45+
46+
const FASTEXP: usize = 800;
47+
48+
#[inline]
49+
fn fastexp(x: f64) -> f64 {
50+
let x = 1. + x/1024.;
51+
x.powi(1024)
52+
}
53+
54+
#[bench]
55+
fn map_fastexp_regular(bench: &mut Bencher)
56+
{
57+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
58+
bench.iter(|| {
59+
a.mapv_inplace(|x| fastexp(x))
60+
});
61+
}
62+
63+
#[bench]
64+
fn rayon_fastexp_regular(bench: &mut Bencher)
65+
{
66+
set_threads();
67+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
68+
bench.iter(|| {
69+
a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x));
70+
});
71+
}
72+
73+
#[bench]
74+
fn map_fastexp_cut(bench: &mut Bencher)
75+
{
76+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
77+
let mut a = a.slice_mut(s![.., ..-1]);
78+
bench.iter(|| {
79+
a.mapv_inplace(|x| fastexp(x))
80+
});
81+
}
82+
83+
#[bench]
84+
fn rayon_fastexp_cut(bench: &mut Bencher)
85+
{
86+
set_threads();
87+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
88+
let mut a = a.slice_mut(s![.., ..-1]);
89+
bench.iter(|| {
90+
a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x));
91+
});
92+
}
93+
94+
#[bench]
95+
fn map_fastexp_by_axis(bench: &mut Bencher)
96+
{
97+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
98+
bench.iter(|| {
99+
for mut sheet in a.axis_iter_mut(Axis(0)) {
100+
sheet.mapv_inplace(fastexp)
101+
}
102+
});
103+
}
104+
105+
#[bench]
106+
fn rayon_fastexp_by_axis(bench: &mut Bencher)
107+
{
108+
set_threads();
109+
let mut a = Array2::<f64>::zeros((FASTEXP, FASTEXP));
110+
bench.iter(|| {
111+
a.axis_iter_mut(Axis(0)).into_par_iter()
112+
.for_each(|mut sheet| sheet.mapv_inplace(fastexp));
113+
});
114+
}

parallel/src/into_impls.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use ndarray::{Array, RcArray, Dimension, ArrayView, ArrayViewMut};
2+
3+
use NdarrayIntoParallelIterator as IntoParallelIterator;
4+
use Parallel;
5+
6+
impl<'a, A, D> IntoParallelIterator for &'a Array<A, D>
7+
where D: Dimension,
8+
A: Sync
9+
{
10+
type Item = &'a A;
11+
type Iter = Parallel<ArrayView<'a, A, D>>;
12+
fn into_par_iter(self) -> Self::Iter {
13+
self.view().into_par_iter()
14+
}
15+
}
16+
17+
// This is allowed: goes through `.view()`
18+
impl<'a, A, D> IntoParallelIterator for &'a RcArray<A, D>
19+
where D: Dimension,
20+
A: Sync
21+
{
22+
type Item = &'a A;
23+
type Iter = Parallel<ArrayView<'a, A, D>>;
24+
fn into_par_iter(self) -> Self::Iter {
25+
self.view().into_par_iter()
26+
}
27+
}
28+
29+
impl<'a, A, D> IntoParallelIterator for &'a mut Array<A, D>
30+
where D: Dimension,
31+
A: Sync + Send
32+
{
33+
type Item = &'a mut A;
34+
type Iter = Parallel<ArrayViewMut<'a, A, D>>;
35+
fn into_par_iter(self) -> Self::Iter {
36+
self.view_mut().into_par_iter()
37+
}
38+
}
39+
40+
// This is allowed: goes through `.view_mut()`, which is unique access
41+
impl<'a, A, D> IntoParallelIterator for &'a mut RcArray<A, D>
42+
where D: Dimension,
43+
A: Sync + Send + Clone,
44+
{
45+
type Item = &'a mut A;
46+
type Iter = Parallel<ArrayViewMut<'a, A, D>>;
47+
fn into_par_iter(self) -> Self::Iter {
48+
self.view_mut().into_par_iter()
49+
}
50+
}

parallel/src/into_traits.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
use rayon::par_iter::ParallelIterator;
3+
4+
pub trait NdarrayIntoParallelIterator {
5+
type Iter: ParallelIterator<Item=Self::Item>;
6+
type Item: Send;
7+
fn into_par_iter(self) -> Self::Iter;
8+
}
9+
10+
pub trait NdarrayIntoParallelRefIterator<'x> {
11+
type Iter: ParallelIterator<Item=Self::Item>;
12+
type Item: Send + 'x;
13+
fn par_iter(&'x self) -> Self::Iter;
14+
}
15+
16+
pub trait NdarrayIntoParallelRefMutIterator<'x> {
17+
type Iter: ParallelIterator<Item=Self::Item>;
18+
type Item: Send + 'x;
19+
fn par_iter_mut(&'x mut self) -> Self::Iter;
20+
}
21+
22+
impl<'data, I: 'data + ?Sized> NdarrayIntoParallelRefIterator<'data> for I
23+
where &'data I: NdarrayIntoParallelIterator
24+
{
25+
type Iter = <&'data I as NdarrayIntoParallelIterator>::Iter;
26+
type Item = <&'data I as NdarrayIntoParallelIterator>::Item;
27+
28+
fn par_iter(&'data self) -> Self::Iter {
29+
self.into_par_iter()
30+
}
31+
}
32+
33+
impl<'data, I: 'data + ?Sized> NdarrayIntoParallelRefMutIterator<'data> for I
34+
where &'data mut I: NdarrayIntoParallelIterator
35+
{
36+
type Iter = <&'data mut I as NdarrayIntoParallelIterator>::Iter;
37+
type Item = <&'data mut I as NdarrayIntoParallelIterator>::Item;
38+
39+
fn par_iter_mut(&'data mut self) -> Self::Iter {
40+
self.into_par_iter()
41+
}
42+
}

parallel/src/lib.rs

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Parallelization features for ndarray.
2+
//!
3+
//! The array views and references to owned arrays all implement
4+
//! `NdarrayIntoParallelIterator` (*); the default parallel iterators (each element
5+
//! by reference or mutable reference) have no ordering guarantee in their
6+
//! parallel implementations.
7+
//!
8+
//! `.axis_iter()` and `.axis_iter_mut()` also have parallel counterparts.
9+
//!
10+
//! (*) This regime of a custom trait instead of rayon’s own is since we
11+
//! use this intermediate ndarray-parallel crate.
12+
//!
13+
//! # Examples
14+
//!
15+
//! Compute the exponential of each element in an array, parallelized.
16+
//!
17+
//! ```
18+
//! extern crate ndarray;
19+
//! extern crate ndarray_parallel;
20+
//!
21+
//! use ndarray::Array2;
22+
//! use ndarray_parallel::prelude::*;
23+
//!
24+
//! fn main() {
25+
//! let mut a = Array2::<f64>::zeros((128, 128));
26+
//! a.par_iter_mut().for_each(|x| *x = x.exp());
27+
//! }
28+
//! ```
29+
//!
30+
//! Use the parallel `.axis_iter()` to compute the sum of each row.
31+
//!
32+
//! ```
33+
//! extern crate ndarray;
34+
//! extern crate ndarray_parallel;
35+
//!
36+
//! use ndarray::Array;
37+
//! use ndarray::Axis;
38+
//! use ndarray_parallel::prelude::*;
39+
//!
40+
//! fn main() {
41+
//! let a = Array::linspace(0., 63., 64).into_shape((4, 16)).unwrap();
42+
//! let mut sums = Vec::new();
43+
//! a.axis_iter(Axis(0))
44+
//! .into_par_iter()
45+
//! .map(|row| row.scalar_sum())
46+
//! .collect_into(&mut sums);
47+
//!
48+
//! assert_eq!(sums, [120., 376., 632., 888.]);
49+
//! }
50+
//! ```
51+
52+
53+
extern crate ndarray;
54+
extern crate rayon;
55+
56+
/// Into- traits for creating parallelized iterators.
57+
pub mod prelude {
58+
// happy and insane; ignorance is bluss
59+
pub use NdarrayIntoParallelIterator;
60+
pub use NdarrayIntoParallelRefIterator;
61+
pub use NdarrayIntoParallelRefMutIterator;
62+
63+
#[doc(no_inline)]
64+
pub use rayon::prelude::{ParallelIterator, IndexedParallelIterator, ExactParallelIterator};
65+
}
66+
67+
pub use par::Parallel;
68+
pub use into_traits::{
69+
NdarrayIntoParallelIterator,
70+
NdarrayIntoParallelRefIterator,
71+
NdarrayIntoParallelRefMutIterator,
72+
};
73+
74+
mod par;
75+
mod into_traits;
76+
mod into_impls;

0 commit comments

Comments
 (0)