From 3bd71d4baec5226384dac201830fb64c8e559356 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 13:48:28 +0100 Subject: [PATCH 01/21] Initial implementation of ParallelIterator for AxisIter --- Cargo.toml | 2 ++ src/iterators/mod.rs | 3 ++ src/iterators/par.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ tests/rayon.rs | 18 ++++++++++ 5 files changed, 103 insertions(+) create mode 100644 src/iterators/par.rs create mode 100644 tests/rayon.rs diff --git a/Cargo.toml b/Cargo.toml index 3daa21ff8..d04d644eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ optional = true blas-sys = { version = "0.6.5", optional = true, default-features = false } matrixmultiply = { version = "0.1.13" } +rayon = { version = "0.5.0", optional = true, default-features = false } + [dependencies.serde] version = "0.8.20" optional = true diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index e379fb96f..762648e55 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -21,6 +21,9 @@ use super::{ Axis, }; +#[cfg(feature = "rayon")] +mod par; + /// Base for array iterators /// /// Iterator element type is `&'a A`. diff --git a/src/iterators/par.rs b/src/iterators/par.rs new file mode 100644 index 000000000..0e1da784b --- /dev/null +++ b/src/iterators/par.rs @@ -0,0 +1,78 @@ + + +use rayon::par_iter::ParallelIterator; +use rayon::par_iter::IndexedParallelIterator; +use rayon::par_iter::ExactParallelIterator; +use rayon::par_iter::BoundedParallelIterator; +use rayon::par_iter::internal::{Consumer, UnindexedConsumer}; +use rayon::par_iter::internal::bridge; +use rayon::par_iter::internal::ProducerCallback; +use rayon::par_iter::internal::Producer; + +use super::AxisIter; +use imp_prelude::*; + + + +impl<'a, A, D> ParallelIterator for AxisIter<'a, A, D> + where D: Dimension, + A: Sync, +{ + type Item = ::Item; + fn drive_unindexed(self, consumer: C) -> C::Result + where C: UnindexedConsumer + { + bridge(self, consumer) + } +} + +impl<'a, A, D> IndexedParallelIterator for AxisIter<'a, A, D> + where D: Dimension, + A: Sync, +{ + fn with_producer(self, callback: Cb) -> Cb::Output + where Cb: ProducerCallback + { + callback.callback(self) + } +} + +impl<'a, A, D> ExactParallelIterator for AxisIter<'a, A, D> + where D: Dimension, + A: Sync, +{ + fn len(&mut self) -> usize { + self.size_hint().0 + } +} + +impl<'a, A, D> BoundedParallelIterator for AxisIter<'a, A, D> + where D: Dimension, + A: Sync, +{ + fn upper_bound(&mut self) -> usize { + ExactParallelIterator::len(self) + } + + fn drive(self, consumer: C) -> C::Result + where C: Consumer + { + bridge(self, consumer) + } +} + +// This is the real magic, I guess + +impl<'a, A, D> Producer for AxisIter<'a, A, D> + where D: Dimension, + A: Sync, +{ + fn cost(&mut self, len: usize) -> f64 { + // FIXME: No idea about what this is + len as f64 + } + + fn split_at(self, i: usize) -> (Self, Self) { + self.split_at(i) + } +} diff --git a/src/lib.rs b/src/lib.rs index 5e049b13e..ad7560ce3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,8 @@ extern crate matrixmultiply; extern crate itertools; extern crate num_traits as libnum; extern crate num_complex; +#[cfg(feature = "rayon")] +extern crate rayon; use std::iter::Zip; use std::marker::PhantomData; diff --git a/tests/rayon.rs b/tests/rayon.rs new file mode 100644 index 000000000..548814f25 --- /dev/null +++ b/tests/rayon.rs @@ -0,0 +1,18 @@ +#![cfg(feature = "rayon")] + +extern crate rayon; +extern crate ndarray; + +use ndarray::prelude::*; + +use rayon::par_iter::ParallelIterator; + +#[test] +fn test_axis_iter() { + let mut a = Array2::::zeros((10240, 10240)); + for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { + v.fill(i as _); + } + let s = ParallelIterator::map(a.axis_iter(Axis(0)), |x| x.scalar_sum()).sum(); + assert_eq!(s, a.scalar_sum()); +} From b64d08a6212e27019553eb73ff2c3ab91c6c83fc Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 13:48:28 +0100 Subject: [PATCH 02/21] Use a separate type for the ParallelIterator --- src/iterators/par.rs | 40 ++++++++++++++++++++++++++++++++++------ tests/rayon.rs | 7 ++++--- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 0e1da784b..0ee9c3af9 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -1,6 +1,7 @@ use rayon::par_iter::ParallelIterator; +use rayon::par_iter::IntoParallelIterator; use rayon::par_iter::IndexedParallelIterator; use rayon::par_iter::ExactParallelIterator; use rayon::par_iter::BoundedParallelIterator; @@ -12,13 +13,40 @@ use rayon::par_iter::internal::Producer; use super::AxisIter; use imp_prelude::*; +/// Parallel iterator wrapper. +pub struct Parallel { + pub iter: I, +} +impl From for Parallel + where I: IntoIterator, +{ + fn from(iter: I) -> Self { + Parallel { + iter: iter.into_iter() + } + } +} -impl<'a, A, D> ParallelIterator for AxisIter<'a, A, D> +impl<'a, A, D> IntoParallelIterator for AxisIter<'a, A, D> where D: Dimension, A: Sync, { type Item = ::Item; + type Iter = Parallel; + fn into_par_iter(self) -> Self::Iter { + Parallel { + iter: self, + } + } +} + + +impl<'a, A, D> ParallelIterator for Parallel> + where D: Dimension, + A: Sync, +{ + type Item = as Iterator>::Item; fn drive_unindexed(self, consumer: C) -> C::Result where C: UnindexedConsumer { @@ -26,27 +54,27 @@ impl<'a, A, D> ParallelIterator for AxisIter<'a, A, D> } } -impl<'a, A, D> IndexedParallelIterator for AxisIter<'a, A, D> +impl<'a, A, D> IndexedParallelIterator for Parallel> where D: Dimension, A: Sync, { fn with_producer(self, callback: Cb) -> Cb::Output where Cb: ProducerCallback { - callback.callback(self) + callback.callback(self.iter) } } -impl<'a, A, D> ExactParallelIterator for AxisIter<'a, A, D> +impl<'a, A, D> ExactParallelIterator for Parallel> where D: Dimension, A: Sync, { fn len(&mut self) -> usize { - self.size_hint().0 + self.iter.len() } } -impl<'a, A, D> BoundedParallelIterator for AxisIter<'a, A, D> +impl<'a, A, D> BoundedParallelIterator for Parallel> where D: Dimension, A: Sync, { diff --git a/tests/rayon.rs b/tests/rayon.rs index 548814f25..6d55c64db 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -5,14 +5,15 @@ extern crate ndarray; use ndarray::prelude::*; -use rayon::par_iter::ParallelIterator; +use rayon::prelude::*; #[test] fn test_axis_iter() { - let mut a = Array2::::zeros((10240, 10240)); + let mut a = Array2::::zeros((1024 * 1024, 100)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); } - let s = ParallelIterator::map(a.axis_iter(Axis(0)), |x| x.scalar_sum()).sum(); + assert_eq!(a.axis_iter(Axis(0)).len(), 1024 * 1024); + let s = a.axis_iter(Axis(0)).into_par_iter().map(|x| x.scalar_sum()).sum(); assert_eq!(s, a.scalar_sum()); } From 7893343abaac7cbf1e4c170a11c0ef77a6b30c44 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 13:48:28 +0100 Subject: [PATCH 03/21] Enable parallel iterator for both AxisIter and AxisIterMut --- src/iterators/par.rs | 124 +++++++++++++++++++++++-------------------- tests/rayon.rs | 11 +++- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 0ee9c3af9..b830efef9 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -11,11 +11,13 @@ use rayon::par_iter::internal::ProducerCallback; use rayon::par_iter::internal::Producer; use super::AxisIter; +use super::AxisIterMut; use imp_prelude::*; /// Parallel iterator wrapper. +#[derive(Copy, Clone, Debug)] pub struct Parallel { - pub iter: I, + iter: I, } impl From for Parallel @@ -28,79 +30,85 @@ impl From for Parallel } } -impl<'a, A, D> IntoParallelIterator for AxisIter<'a, A, D> - where D: Dimension, - A: Sync, -{ - type Item = ::Item; - type Iter = Parallel; - fn into_par_iter(self) -> Self::Iter { - Parallel { - iter: self, +macro_rules! par_iter_wrapper { + // thread_bounds are either Sync or Send + Sync + ($iter_name:ident, [$($thread_bounds:tt)*]) => { + impl<'a, A, D> IntoParallelIterator for $iter_name<'a, A, D> + where D: Dimension, + A: $($thread_bounds)*, + { + type Item = ::Item; + type Iter = Parallel; + fn into_par_iter(self) -> Self::Iter { + Parallel::from(self) } } -} -impl<'a, A, D> ParallelIterator for Parallel> - where D: Dimension, - A: Sync, -{ - type Item = as Iterator>::Item; - fn drive_unindexed(self, consumer: C) -> C::Result - where C: UnindexedConsumer + impl<'a, A, D> ParallelIterator for Parallel<$iter_name<'a, A, D>> + where D: Dimension, + A: $($thread_bounds)*, { - bridge(self, consumer) + type Item = <$iter_name<'a, A, D> as Iterator>::Item; + fn drive_unindexed(self, consumer: C) -> C::Result + where C: UnindexedConsumer + { + bridge(self, consumer) + } } -} -impl<'a, A, D> IndexedParallelIterator for Parallel> - where D: Dimension, - A: Sync, -{ - fn with_producer(self, callback: Cb) -> Cb::Output - where Cb: ProducerCallback + impl<'a, A, D> IndexedParallelIterator for Parallel<$iter_name<'a, A, D>> + where D: Dimension, + A: $($thread_bounds)*, { - callback.callback(self.iter) - } -} - -impl<'a, A, D> ExactParallelIterator for Parallel> - where D: Dimension, - A: Sync, -{ - fn len(&mut self) -> usize { - self.iter.len() + fn with_producer(self, callback: Cb) -> Cb::Output + where Cb: ProducerCallback + { + callback.callback(self.iter) + } } -} -impl<'a, A, D> BoundedParallelIterator for Parallel> - where D: Dimension, - A: Sync, -{ - fn upper_bound(&mut self) -> usize { - ExactParallelIterator::len(self) + impl<'a, A, D> ExactParallelIterator for Parallel<$iter_name<'a, A, D>> + where D: Dimension, + A: $($thread_bounds)*, + { + fn len(&mut self) -> usize { + ExactSizeIterator::len(&self.iter) + } } - fn drive(self, consumer: C) -> C::Result - where C: Consumer + impl<'a, A, D> BoundedParallelIterator for Parallel<$iter_name<'a, A, D>> + where D: Dimension, + A: $($thread_bounds)*, { - bridge(self, consumer) + fn upper_bound(&mut self) -> usize { + ExactSizeIterator::len(&self.iter) + } + + fn drive(self, consumer: C) -> C::Result + where C: Consumer + { + bridge(self, consumer) + } } -} -// This is the real magic, I guess + // This is the real magic, I guess -impl<'a, A, D> Producer for AxisIter<'a, A, D> - where D: Dimension, - A: Sync, -{ - fn cost(&mut self, len: usize) -> f64 { - // FIXME: No idea about what this is - len as f64 - } + impl<'a, A, D> Producer for $iter_name<'a, A, D> + where D: Dimension, + A: $($thread_bounds)*, + { + fn cost(&mut self, len: usize) -> f64 { + // FIXME: No idea about what this is + len as f64 + } - fn split_at(self, i: usize) -> (Self, Self) { - self.split_at(i) + fn split_at(self, i: usize) -> (Self, Self) { + self.split_at(i) + } + } } } + +par_iter_wrapper!(AxisIter, [Sync]); +par_iter_wrapper!(AxisIterMut, [Send + Sync]); diff --git a/tests/rayon.rs b/tests/rayon.rs index 6d55c64db..53ed9781a 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -1,7 +1,7 @@ #![cfg(feature = "rayon")] extern crate rayon; -extern crate ndarray; +#[macro_use(s)] extern crate ndarray; use ndarray::prelude::*; @@ -17,3 +17,12 @@ fn test_axis_iter() { let s = a.axis_iter(Axis(0)).into_par_iter().map(|x| x.scalar_sum()).sum(); assert_eq!(s, a.scalar_sum()); } + +#[test] +fn test_axis_iter_mut() { + let mut a = Array2::::zeros((1024 * 1024, 100)); + a.axis_iter_mut(Axis(0)).into_par_iter().enumerate().for_each(|(i, mut v)| v.fill(i as _)); + assert_eq!(a.scalar_sum(), + (0..a.len_of(Axis(0))).map(|n| n as u32 * a.len_of(Axis(1)) as u32).sum::()); + println!("{:?}", a.slice(s![..10, ..10])); +} From 5ab3e5de0f4c59e9371061260f0b2d30f1ca6794 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 13:48:28 +0100 Subject: [PATCH 04/21] Update tests for parallel axis iterators --- tests/rayon.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/rayon.rs b/tests/rayon.rs index 53ed9781a..b3b539d98 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -7,22 +7,26 @@ use ndarray::prelude::*; use rayon::prelude::*; +const M: usize = 1024 * 10; +const N: usize = 100; + #[test] fn test_axis_iter() { - let mut a = Array2::::zeros((1024 * 1024, 100)); + let mut a = Array2::::zeros((M, N)); for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); } - assert_eq!(a.axis_iter(Axis(0)).len(), 1024 * 1024); + assert_eq!(a.axis_iter(Axis(0)).len(), M); let s = a.axis_iter(Axis(0)).into_par_iter().map(|x| x.scalar_sum()).sum(); + println!("{:?}", a.slice(s![..10, ..5])); assert_eq!(s, a.scalar_sum()); } #[test] fn test_axis_iter_mut() { - let mut a = Array2::::zeros((1024 * 1024, 100)); - a.axis_iter_mut(Axis(0)).into_par_iter().enumerate().for_each(|(i, mut v)| v.fill(i as _)); - assert_eq!(a.scalar_sum(), - (0..a.len_of(Axis(0))).map(|n| n as u32 * a.len_of(Axis(1)) as u32).sum::()); - println!("{:?}", a.slice(s![..10, ..10])); + let mut a = Array::linspace(0., 1.0f64, M * N).into_shape((M, N)).unwrap(); + let b = a.mapv(|x| x.exp()); + a.axis_iter_mut(Axis(0)).into_par_iter().for_each(|mut v| v.mapv_inplace(|x| x.exp())); + println!("{:?}", a.slice(s![..10, ..5])); + assert!(a.all_close(&b, 0.001)); } From 2dbf1a7c681f8fd0a3b77f99c7a3ff234ddf2457 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 13:48:28 +0100 Subject: [PATCH 05/21] Enable "rayon" feature in CI and docs --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d04d644eb..6ef66fff9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,10 +50,10 @@ blas = ["blas-sys"] # These features are used for testing blas-openblas-sys = ["blas"] -test = ["blas-openblas-sys"] +test = ["blas-openblas-sys", "rayon"] # This feature is used for docs -docs = ["rustc-serialize", "serde"] +docs = ["rustc-serialize", "serde", "rayon"] [profile.release] [profile.bench] From c109dc3f8f9cf2c63240b517e2bc7bff16f5876e Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 13:48:28 +0100 Subject: [PATCH 06/21] Expose `Parallel` in docs and document crate feature --- README.rst | 5 +++++ src/iterators/mod.rs | 2 +- src/iterators/par.rs | 8 ++++++-- src/lib.rs | 5 +++++ src/par/mod.rs | 6 ++++++ 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 src/par/mod.rs diff --git a/README.rst b/README.rst index a771f307f..4db9999b1 100644 --- a/README.rst +++ b/README.rst @@ -69,6 +69,11 @@ your `Cargo.toml`. Uses ``blas-sys`` for pluggable backend, which needs to be configured separately. +- ``rayon`` + + - Optional, compatible with Rust stable + - Implement rayon 0.5 parallelization traits for ``AxisIter``. + How to use with cargo:: [dependencies] diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 762648e55..4f91c4b61 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -22,7 +22,7 @@ use super::{ }; #[cfg(feature = "rayon")] -mod par; +pub mod par; /// Base for array iterators /// diff --git a/src/iterators/par.rs b/src/iterators/par.rs index b830efef9..50a1ca954 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -14,7 +14,9 @@ use super::AxisIter; use super::AxisIterMut; use imp_prelude::*; -/// Parallel iterator wrapper. +/// Iterator wrapper for parallelized implementations. +/// +/// **Requires crate feature `"rayon"`** #[derive(Copy, Clone, Debug)] pub struct Parallel { iter: I, @@ -33,6 +35,9 @@ impl From for Parallel macro_rules! par_iter_wrapper { // thread_bounds are either Sync or Send + Sync ($iter_name:ident, [$($thread_bounds:tt)*]) => { + /// This iterator can be turned into a parallel iterator (rayon crate). + /// + /// **Requires crate feature `"rayon"`** impl<'a, A, D> IntoParallelIterator for $iter_name<'a, A, D> where D: Dimension, A: $($thread_bounds)*, @@ -44,7 +49,6 @@ macro_rules! par_iter_wrapper { } } - impl<'a, A, D> ParallelIterator for Parallel<$iter_name<'a, A, D>> where D: Dimension, A: $($thread_bounds)*, diff --git a/src/lib.rs b/src/lib.rs index ad7560ce3..94e027c67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,9 @@ //! - Enable transparent BLAS support for matrix multiplication. //! Uses ``blas-sys`` for pluggable backend, which needs to be configured //! separately. +//! - `rayon` +//! - Optional. +//! - Implement rayon 0.5 parallelization traits for `AxisIter`. //! #[cfg(feature = "serde")] @@ -126,6 +129,8 @@ mod array_serde; mod array_serialize; mod arrayformat; mod data_traits; +#[cfg(feature = "rayon")] +pub mod par; pub use aliases::*; diff --git a/src/par/mod.rs b/src/par/mod.rs new file mode 100644 index 000000000..717e35775 --- /dev/null +++ b/src/par/mod.rs @@ -0,0 +1,6 @@ + +//! Parallelization features for ndarray. +//! +//! **Requires crate feature `"rayon"`** + +pub use iterators::par::Parallel; From b772b99120f0bd50faf4021ffa5bc92445226747 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 14 Dec 2016 23:28:02 +0100 Subject: [PATCH 07/21] Use rayon-git to implement IntoParallelIterator for array views --- Cargo.toml | 2 +- src/iterators/par.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++ tests/rayon.rs | 11 +++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6ef66fff9..b79d015d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ optional = true blas-sys = { version = "0.6.5", optional = true, default-features = false } matrixmultiply = { version = "0.1.13" } -rayon = { version = "0.5.0", optional = true, default-features = false } +rayon = { git = "https://github.com/nikomatsakis/rayon", optional = true } [dependencies.serde] version = "0.8.20" diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 50a1ca954..4c3ce327a 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -7,8 +7,10 @@ use rayon::par_iter::ExactParallelIterator; use rayon::par_iter::BoundedParallelIterator; use rayon::par_iter::internal::{Consumer, UnindexedConsumer}; use rayon::par_iter::internal::bridge; +use rayon::par_iter::internal::bridge_unindexed; use rayon::par_iter::internal::ProducerCallback; use rayon::par_iter::internal::Producer; +use rayon::par_iter::internal::UnindexedProducer; use super::AxisIter; use super::AxisIterMut; @@ -116,3 +118,55 @@ macro_rules! par_iter_wrapper { par_iter_wrapper!(AxisIter, [Sync]); par_iter_wrapper!(AxisIterMut, [Send + Sync]); + +macro_rules! par_iter_view_wrapper { + // thread_bounds are either Sync or Send + Sync + ($view_name:ident, [$($thread_bounds:tt)*]) => { + impl<'a, A, D> IntoParallelIterator for $view_name<'a, A, D> + where D: Dimension, + A: $($thread_bounds)*, + { + type Item = ::Item; + type Iter = Parallel; + fn into_par_iter(self) -> Self::Iter { + Parallel { + iter: self, + } + } + } + + + impl<'a, A, D> ParallelIterator for Parallel<$view_name<'a, A, D>> + where D: Dimension, + A: $($thread_bounds)*, + { + type Item = <$view_name<'a, A, D> as IntoIterator>::Item; + fn drive_unindexed(self, consumer: C) -> C::Result + where C: UnindexedConsumer + { + bridge_unindexed(self.iter, consumer) + } + } + + impl<'a, A, D> UnindexedProducer for $view_name<'a, A, D> + where D: Dimension, + A: $($thread_bounds)*, + { + fn can_split(&self) -> bool { + self.len() > 1 + } + + fn split(self) -> (Self, Self) { + let max_axis = self.max_stride_axis(); + let mid = self.len_of(max_axis); + self.split_at(max_axis, mid) + } + } + + } +} + +use super::Iter; + +par_iter_view_wrapper!(ArrayView, [Sync]); +par_iter_view_wrapper!(ArrayViewMut, [Sync + Send]); diff --git a/tests/rayon.rs b/tests/rayon.rs index b3b539d98..82875c875 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -30,3 +30,14 @@ fn test_axis_iter_mut() { println!("{:?}", a.slice(s![..10, ..5])); assert!(a.all_close(&b, 0.001)); } + +#[test] +fn test_regular_iter() { + let mut a = Array2::::zeros((M, N)); + for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { + v.fill(i as _); + } + let s = a.view().into_par_iter().map(|&x| x).sum(); + println!("{:?}", a.slice(s![..10, ..5])); + assert_eq!(s, a.scalar_sum()); +} From 09a1e996636e986b838e31bc7d9be1b0efd46cad Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 15:38:17 +0100 Subject: [PATCH 08/21] Use rayon's .fold_using() --- Cargo.toml | 2 +- benches/rayon.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/iterators/par.rs | 21 ++++++++++++--- 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 benches/rayon.rs diff --git a/Cargo.toml b/Cargo.toml index b79d015d4..2a3fbba7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ optional = true blas-sys = { version = "0.6.5", optional = true, default-features = false } matrixmultiply = { version = "0.1.13" } -rayon = { git = "https://github.com/nikomatsakis/rayon", optional = true } +rayon = { git = "https://github.com/bluss/rayon", branch = "fold-using", optional = true } [dependencies.serde] version = "0.8.20" diff --git a/benches/rayon.rs b/benches/rayon.rs new file mode 100644 index 000000000..8db74740e --- /dev/null +++ b/benches/rayon.rs @@ -0,0 +1,62 @@ + +#![feature(test)] + +extern crate test; +use test::Bencher; + +#[macro_use(s)] +extern crate ndarray; +use ndarray::prelude::*; + +extern crate rayon; +use rayon::prelude::*; + +const EXP_N: usize = 128; + +#[bench] +fn map_exp_regular(bench: &mut Bencher) +{ + let mut a = Array2::::zeros((EXP_N, EXP_N)); + a.swap_axes(0, 1); + bench.iter(|| { + a.mapv_inplace(|x| x.exp()); + }); +} + +#[bench] +fn rayon_exp_regular(bench: &mut Bencher) +{ + let mut a = Array2::::zeros((EXP_N, EXP_N)); + a.swap_axes(0, 1); + bench.iter(|| { + a.view_mut().into_par_iter().for_each(|x| *x = x.exp()); + }); +} + +const FASTEXP: usize = 900; + +#[inline] +fn fastexp(x: f64) -> f64 { + let x = 1. + x/1024.; + x.powi(1024) +} + +#[bench] +fn map_fastexp_regular(bench: &mut Bencher) +{ + let mut a = Array2::::zeros((FASTEXP, FASTEXP)); + let mut a = a.slice_mut(s![.., ..-1]); + bench.iter(|| { + a.mapv_inplace(|x| fastexp(x)) + }); +} + +#[bench] +fn rayon_fastexp_regular(bench: &mut Bencher) +{ + let mut a = Array2::::zeros((FASTEXP, FASTEXP)); + let mut a = a.slice_mut(s![.., ..-1]); + bench.iter(|| { + a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); + }); +} diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 4c3ce327a..73f62861d 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -11,6 +11,7 @@ use rayon::par_iter::internal::bridge_unindexed; use rayon::par_iter::internal::ProducerCallback; use rayon::par_iter::internal::Producer; use rayon::par_iter::internal::UnindexedProducer; +use rayon::par_iter::internal::Folder; use super::AxisIter; use super::AxisIterMut; @@ -146,6 +147,10 @@ macro_rules! par_iter_view_wrapper { { bridge_unindexed(self.iter, consumer) } + + fn opt_len(&mut self) -> Option { + Some(self.iter.len()) + } } impl<'a, A, D> UnindexedProducer for $view_name<'a, A, D> @@ -153,13 +158,23 @@ macro_rules! par_iter_view_wrapper { A: $($thread_bounds)*, { fn can_split(&self) -> bool { - self.len() > 1 + // FIXME: Bad to hardcode any limit here + self.len() > 128 } fn split(self) -> (Self, Self) { let max_axis = self.max_stride_axis(); - let mid = self.len_of(max_axis); - self.split_at(max_axis, mid) + let mid = self.len_of(max_axis) / 2; + let (a, b) = self.split_at(max_axis, mid); + //println!("Split along axis {:?} at {}", max_axis, mid); + //println!("Result shapes {:?}, {:?}", a.shape(), b.shape()); + (a, b) + } + + fn fold_with(self, folder: F) -> F + where F: Folder, + { + self.into_iter().fold(folder, |f, elt| f.consume(elt)) } } From a4fe86a2137d764b723f09ecb38f0422c19cec01 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 19:27:48 +0100 Subject: [PATCH 09/21] Update parameters in rayon impl / benchmark Use split threshold 1, and use one thread per core. --- Cargo.toml | 3 +++ benches/rayon.rs | 13 ++++++++++++- src/iterators/par.rs | 5 ++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a3fbba7c..bd432fb7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,9 @@ rayon = { git = "https://github.com/bluss/rayon", branch = "fold-using", optiona version = "0.8.20" optional = true +[dev-dependencies] +num_cpus = "1.2" + [features] blas = ["blas-sys"] diff --git a/benches/rayon.rs b/benches/rayon.rs index 8db74740e..55c067386 100644 --- a/benches/rayon.rs +++ b/benches/rayon.rs @@ -1,6 +1,7 @@ #![feature(test)] +extern crate num_cpus; extern crate test; use test::Bencher; @@ -13,6 +14,14 @@ use rayon::prelude::*; const EXP_N: usize = 128; +use std::cmp::max; + +fn set_threads() { + let n = max(1, num_cpus::get() / 2); + let cfg = rayon::Configuration::new().set_num_threads(n); + let _ = rayon::initialize(cfg); +} + #[bench] fn map_exp_regular(bench: &mut Bencher) { @@ -26,6 +35,7 @@ fn map_exp_regular(bench: &mut Bencher) #[bench] fn rayon_exp_regular(bench: &mut Bencher) { + set_threads(); let mut a = Array2::::zeros((EXP_N, EXP_N)); a.swap_axes(0, 1); bench.iter(|| { @@ -33,7 +43,7 @@ fn rayon_exp_regular(bench: &mut Bencher) }); } -const FASTEXP: usize = 900; +const FASTEXP: usize = 800; #[inline] fn fastexp(x: f64) -> f64 { @@ -44,6 +54,7 @@ fn fastexp(x: f64) -> f64 { #[bench] fn map_fastexp_regular(bench: &mut Bencher) { + set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); bench.iter(|| { diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 73f62861d..17ac52f16 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -159,15 +159,14 @@ macro_rules! par_iter_view_wrapper { { fn can_split(&self) -> bool { // FIXME: Bad to hardcode any limit here - self.len() > 128 + self.len() > 1 } fn split(self) -> (Self, Self) { let max_axis = self.max_stride_axis(); let mid = self.len_of(max_axis) / 2; let (a, b) = self.split_at(max_axis, mid); - //println!("Split along axis {:?} at {}", max_axis, mid); - //println!("Result shapes {:?}, {:?}", a.shape(), b.shape()); + //println!("Split along axis {:?} at {}\nshapes {:?}, {:?}", max_axis, mid, a.shape(), b.shape()); (a, b) } From 683f17da3b74dc587324f6423651b38ff15aed75 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 23:39:15 +0100 Subject: [PATCH 10/21] Use `move` on closure in fold_with --- src/iterators/par.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 17ac52f16..98624d86a 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -173,7 +173,7 @@ macro_rules! par_iter_view_wrapper { fn fold_with(self, folder: F) -> F where F: Folder, { - self.into_iter().fold(folder, |f, elt| f.consume(elt)) + self.into_iter().fold(folder, move |f, elt| f.consume(elt)) } } From 97e94bee528b1c1d1c0d1f596310b2e2acc2fa65 Mon Sep 17 00:00:00 2001 From: bluss Date: Mon, 19 Dec 2016 23:58:24 +0100 Subject: [PATCH 11/21] Use an unordered iteration for par_iter for array views --- src/impl_methods.rs | 6 ++--- src/impl_views.rs | 58 ++++++++++++++++++++++++++++++++++++++++++-- src/iterators/par.rs | 4 ++- src/lib.rs | 3 +++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 1d2171752..d8a14537e 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -23,6 +23,8 @@ use super::ZipExt; use dimension::IntoDimension; use dimension::{axes_of, Axes}; +use impl_views::ArrayViewPrivate; + use { NdIndex, AxisChunksIter, @@ -688,10 +690,6 @@ impl ArrayBase where S: Data, D: Dimension is_standard_layout(&self.dim, &self.strides) } - fn is_contiguous(&self) -> bool { - D::is_contiguous(&self.dim, &self.strides) - } - /// Return a pointer to the first element in the array. /// /// Raw access to array elements needs to follow the strided indexing diff --git a/src/impl_views.rs b/src/impl_views.rs index 35a355adb..d137dca0c 100644 --- a/src/impl_views.rs +++ b/src/impl_views.rs @@ -14,6 +14,62 @@ use error::ShapeError; use StrideShape; +pub trait ArrayViewPrivate { + type Item; + type Slice: IntoIterator; + unsafe fn into_slice_memory_order(self) -> Self::Slice; + + fn into_fold(self, acc: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc; +} + +impl<'a, A, D> ArrayViewPrivate for ArrayView<'a, A, D> + where D: Dimension, +{ + type Item = &'a A; + type Slice = &'a [A]; + unsafe fn into_slice_memory_order(self) -> Self::Slice { + debug_assert!(self.is_contiguous()); + slice::from_raw_parts(self.ptr, self.len()) + } + + fn into_fold(self, acc: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + if self.is_contiguous() { + unsafe { + self.into_slice_memory_order().into_iter().fold(acc, f) + } + } else { + self.into_iter().fold(acc, f) + } + } +} + + +impl<'a, A, D> ArrayViewPrivate for ArrayViewMut<'a, A, D> + where D: Dimension, +{ + type Item = &'a mut A; + type Slice = &'a mut [A]; + unsafe fn into_slice_memory_order(self) -> Self::Slice { + debug_assert!(self.is_contiguous()); + slice::from_raw_parts_mut(self.ptr, self.len()) + } + + fn into_fold(self, acc: Acc, f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + if self.is_contiguous() { + unsafe { + self.into_slice_memory_order().into_iter().fold(acc, f) + } + } else { + self.into_iter().fold(acc, f) + } + } +} + /// # Methods Specific to Array Views /// /// Methods for read-only array views `ArrayView<'a, A, D>` @@ -126,7 +182,6 @@ impl<'a, A, D> ArrayBase, D> None } } - } /// Methods for read-write array views `ArrayViewMut<'a, A, D>` @@ -237,6 +292,5 @@ impl<'a, A, D> ArrayBase, D> None } } - } diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 98624d86a..f8cae1fcd 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -120,6 +120,8 @@ macro_rules! par_iter_wrapper { par_iter_wrapper!(AxisIter, [Sync]); par_iter_wrapper!(AxisIterMut, [Send + Sync]); +use impl_views::ArrayViewPrivate; + macro_rules! par_iter_view_wrapper { // thread_bounds are either Sync or Send + Sync ($view_name:ident, [$($thread_bounds:tt)*]) => { @@ -173,7 +175,7 @@ macro_rules! par_iter_view_wrapper { fn fold_with(self, folder: F) -> F where F: Folder, { - self.into_iter().fold(folder, move |f, elt| f.consume(elt)) + self.into_fold(folder, move |f, elt| f.consume(elt)) } } diff --git a/src/lib.rs b/src/lib.rs index 94e027c67..bc72dbd18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -587,6 +587,9 @@ impl ArrayBase } } + fn is_contiguous(&self) -> bool { + D::is_contiguous(&self.dim, &self.strides) + } /// Apply closure `f` to each element in the array, in whatever /// order is the fastest to visit. From 41e0299586211907d72402a4ebb6cfe07abb184f Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 20 Dec 2016 01:04:48 +0100 Subject: [PATCH 12/21] Remove `From` impl for Parallel --- src/iterators/par.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index f8cae1fcd..4229b65b7 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -25,16 +25,6 @@ pub struct Parallel { iter: I, } -impl From for Parallel - where I: IntoIterator, -{ - fn from(iter: I) -> Self { - Parallel { - iter: iter.into_iter() - } - } -} - macro_rules! par_iter_wrapper { // thread_bounds are either Sync or Send + Sync ($iter_name:ident, [$($thread_bounds:tt)*]) => { @@ -48,7 +38,9 @@ macro_rules! par_iter_wrapper { type Item = ::Item; type Iter = Parallel; fn into_par_iter(self) -> Self::Iter { - Parallel::from(self) + Parallel { + iter: self, + } } } From 3224843822fba49a5d1b40cf2d6fbabbba41edc5 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 16:46:35 +0100 Subject: [PATCH 13/21] Update for rayon 0.6.0 --- Cargo.toml | 2 +- src/iterators/par.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd432fb7a..3a3bb097e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ optional = true blas-sys = { version = "0.6.5", optional = true, default-features = false } matrixmultiply = { version = "0.1.13" } -rayon = { git = "https://github.com/bluss/rayon", branch = "fold-using", optional = true } +rayon = { version = "0.6.0", optional = true } [dependencies.serde] version = "0.8.20" diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 4229b65b7..f98d82d80 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -54,6 +54,10 @@ macro_rules! par_iter_wrapper { { bridge(self, consumer) } + + fn opt_len(&mut self) -> Option { + Some(self.iter.len()) + } } impl<'a, A, D> IndexedParallelIterator for Parallel<$iter_name<'a, A, D>> @@ -152,7 +156,6 @@ macro_rules! par_iter_view_wrapper { A: $($thread_bounds)*, { fn can_split(&self) -> bool { - // FIXME: Bad to hardcode any limit here self.len() > 1 } @@ -164,6 +167,7 @@ macro_rules! par_iter_view_wrapper { (a, b) } + #[cfg(rayon_fold_with)] fn fold_with(self, folder: F) -> F where F: Folder, { From 7bf56ae0affc68f67aa7d7dc331bf5b15405e600 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 16:46:55 +0100 Subject: [PATCH 14/21] Update rayon benchmarks --- benches/rayon.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/benches/rayon.rs b/benches/rayon.rs index 55c067386..e79447174 100644 --- a/benches/rayon.rs +++ b/benches/rayon.rs @@ -53,8 +53,26 @@ fn fastexp(x: f64) -> f64 { #[bench] fn map_fastexp_regular(bench: &mut Bencher) +{ + let mut a = Array2::::zeros((FASTEXP, FASTEXP)); + bench.iter(|| { + a.mapv_inplace(|x| fastexp(x)) + }); +} + +#[bench] +fn rayon_fastexp_regular(bench: &mut Bencher) { set_threads(); + let mut a = Array2::::zeros((FASTEXP, FASTEXP)); + bench.iter(|| { + a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); + }); +} + +#[bench] +fn map_fastexp_cut(bench: &mut Bencher) +{ let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); bench.iter(|| { @@ -63,8 +81,9 @@ fn map_fastexp_regular(bench: &mut Bencher) } #[bench] -fn rayon_fastexp_regular(bench: &mut Bencher) +fn rayon_fastexp_cut(bench: &mut Bencher) { + set_threads(); let mut a = Array2::::zeros((FASTEXP, FASTEXP)); let mut a = a.slice_mut(s![.., ..-1]); bench.iter(|| { From e3c0ba847ed1d7402ccaa820cbbd50684af35ee3 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:05:02 +0100 Subject: [PATCH 15/21] Add benchmark for AxisIterMut split --- benches/rayon.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/benches/rayon.rs b/benches/rayon.rs index e79447174..f51435df2 100644 --- a/benches/rayon.rs +++ b/benches/rayon.rs @@ -90,3 +90,25 @@ fn rayon_fastexp_cut(bench: &mut Bencher) a.view_mut().into_par_iter().for_each(|x| *x = fastexp(*x)); }); } + +#[bench] +fn map_fastexp_by_axis(bench: &mut Bencher) +{ + let mut a = Array2::::zeros((FASTEXP, FASTEXP)); + bench.iter(|| { + for mut sheet in a.axis_iter_mut(Axis(0)) { + sheet.mapv_inplace(fastexp) + } + }); +} + +#[bench] +fn rayon_fastexp_by_axis(bench: &mut Bencher) +{ + set_threads(); + let mut a = Array2::::zeros((FASTEXP, FASTEXP)); + bench.iter(|| { + a.axis_iter_mut(Axis(0)).into_par_iter() + .for_each(|mut sheet| sheet.mapv_inplace(fastexp)); + }); +} From 21d34f411fa1c41c9b7f83f5d89637478a33221a Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:06:31 +0100 Subject: [PATCH 16/21] Update README for rayon feature --- README.rst | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4db9999b1..ff018ea78 100644 --- a/README.rst +++ b/README.rst @@ -72,7 +72,7 @@ your `Cargo.toml`. - ``rayon`` - Optional, compatible with Rust stable - - Implement rayon 0.5 parallelization traits for ``AxisIter``. + - Implement rayon 0.6 parallelization. How to use with cargo:: diff --git a/src/lib.rs b/src/lib.rs index bc72dbd18..4bfb88154 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ //! separately. //! - `rayon` //! - Optional. -//! - Implement rayon 0.5 parallelization traits for `AxisIter`. +//! - Implement rayon 0.6 parallelization. //! #[cfg(feature = "serde")] From c0bfb8ff133386a491e9866bee32f9e18a31115e Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:20:15 +0100 Subject: [PATCH 17/21] Rm unused import in methods --- src/impl_methods.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index d8a14537e..d571dc949 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -23,8 +23,6 @@ use super::ZipExt; use dimension::IntoDimension; use dimension::{axes_of, Axes}; -use impl_views::ArrayViewPrivate; - use { NdIndex, AxisChunksIter, From e99d8db191165bb77bb4b90b895c4fc24525dd8d Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:20:35 +0100 Subject: [PATCH 18/21] rm import in par --- src/iterators/par.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index f98d82d80..e0d46d68f 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -116,8 +116,6 @@ macro_rules! par_iter_wrapper { par_iter_wrapper!(AxisIter, [Sync]); par_iter_wrapper!(AxisIterMut, [Send + Sync]); -use impl_views::ArrayViewPrivate; - macro_rules! par_iter_view_wrapper { // thread_bounds are either Sync or Send + Sync ($view_name:ident, [$($thread_bounds:tt)*]) => { From 0ff1153b8bd7959e0170df051f55df46557a0c48 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:21:22 +0100 Subject: [PATCH 19/21] Rm use Iter in par --- src/iterators/par.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index e0d46d68f..25dc175ab 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -176,7 +176,5 @@ macro_rules! par_iter_view_wrapper { } } -use super::Iter; - par_iter_view_wrapper!(ArrayView, [Sync]); par_iter_view_wrapper!(ArrayViewMut, [Sync + Send]); From c830f48a15378dc2213a81c1200c8772cebc65f8 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:21:46 +0100 Subject: [PATCH 20/21] Add another cfg to par --- src/iterators/par.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 25dc175ab..292e532cb 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -11,6 +11,7 @@ use rayon::par_iter::internal::bridge_unindexed; use rayon::par_iter::internal::ProducerCallback; use rayon::par_iter::internal::Producer; use rayon::par_iter::internal::UnindexedProducer; +#[cfg(rayon_fold_with)] use rayon::par_iter::internal::Folder; use super::AxisIter; From 7e872675de35b1b9e1c4147a8d331deedc891fbe Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 21 Dec 2016 17:56:45 +0100 Subject: [PATCH 21/21] Implement into parallel iterator for array, rcarray --- src/iterators/par.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ tests/rayon.rs | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/iterators/par.rs b/src/iterators/par.rs index 292e532cb..c0ec90db1 100644 --- a/src/iterators/par.rs +++ b/src/iterators/par.rs @@ -117,6 +117,50 @@ macro_rules! par_iter_wrapper { par_iter_wrapper!(AxisIter, [Sync]); par_iter_wrapper!(AxisIterMut, [Send + Sync]); +impl<'a, A, D> IntoParallelIterator for &'a Array + where D: Dimension, + A: Sync +{ + type Item = &'a A; + type Iter = Parallel>; + fn into_par_iter(self) -> Self::Iter { + self.view().into_par_iter() + } +} + +impl<'a, A, D> IntoParallelIterator for &'a RcArray + where D: Dimension, + A: Sync +{ + type Item = &'a A; + type Iter = Parallel>; + fn into_par_iter(self) -> Self::Iter { + self.view().into_par_iter() + } +} + +impl<'a, A, D> IntoParallelIterator for &'a mut Array + where D: Dimension, + A: Sync + Send +{ + type Item = &'a mut A; + type Iter = Parallel>; + fn into_par_iter(self) -> Self::Iter { + self.view_mut().into_par_iter() + } +} + +impl<'a, A, D> IntoParallelIterator for &'a mut RcArray + where D: Dimension, + A: Sync + Send + Clone, +{ + type Item = &'a mut A; + type Iter = Parallel>; + fn into_par_iter(self) -> Self::Iter { + self.view_mut().into_par_iter() + } +} + macro_rules! par_iter_view_wrapper { // thread_bounds are either Sync or Send + Sync ($view_name:ident, [$($thread_bounds:tt)*]) => { diff --git a/tests/rayon.rs b/tests/rayon.rs index 82875c875..8ff19ae84 100644 --- a/tests/rayon.rs +++ b/tests/rayon.rs @@ -37,7 +37,7 @@ fn test_regular_iter() { for (i, mut v) in a.axis_iter_mut(Axis(0)).enumerate() { v.fill(i as _); } - let s = a.view().into_par_iter().map(|&x| x).sum(); + let s = a.par_iter().map(|&x| x).sum(); println!("{:?}", a.slice(s![..10, ..5])); assert_eq!(s, a.scalar_sum()); }