From 164d753d4eea4ad0431cdb6fd19132e295133452 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Mar 2017 22:54:11 +0200 Subject: [PATCH 1/6] Add ixdyn bench --- benches/bench1.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/benches/bench1.rs b/benches/bench1.rs index 06127dce1..3860b7e21 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -438,6 +438,19 @@ fn add_2d_strided(bench: &mut test::Bencher) }); } +#[bench] +fn add_2d_strided_dyn(bench: &mut test::Bencher) +{ + let mut a = Array::::zeros(&[64, 64 * 2][..]); + let mut a = a.slice_mut(s![.., ..;2]); + let b = Array::::zeros(&[64, 64][..]); + let bv = b.view(); + bench.iter(|| { + a += &bv; + }); +} + + #[bench] fn add_2d_zip_strided(bench: &mut test::Bencher) { From b4945c554346be883b8ba18f0581b02489aa79a7 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Mar 2017 22:54:11 +0200 Subject: [PATCH 2/6] Dimension: inline for IxDyn --- src/dimension/dimension_trait.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index ac091bf3c..3d4b9fb6c 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -712,8 +712,11 @@ unsafe impl Dimension for IxDyn { type SliceArg = [Si]; type Pattern = Self; + #[inline] fn ndim(&self) -> usize { self.ix().len() } + #[inline] fn slice(&self) -> &[Ix] { self.ix() } + #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } #[inline] fn into_pattern(self) -> Self::Pattern { From 5aa7db1c16b9a3d806fcede47d98abaf3e14df63 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Mar 2017 22:54:11 +0200 Subject: [PATCH 3/6] Add tests for Ix0 --- tests/ix0.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/ix0.rs diff --git a/tests/ix0.rs b/tests/ix0.rs new file mode 100644 index 000000000..eb96ea6bf --- /dev/null +++ b/tests/ix0.rs @@ -0,0 +1,52 @@ + +extern crate ndarray; + +use ndarray::Array; +use ndarray::Ix0; +use ndarray::ShapeBuilder; + +#[test] +fn test_ix0() { + let mut a = Array::zeros(Ix0()); + assert_eq!(a[()], 0.); + a[()] = 1.; + assert_eq!(a[()], 1.); + assert_eq!(a.len(), 1); + assert_eq!(a.as_slice().unwrap(), &[1.]); + + let mut a = Array::zeros(Ix0().f()); + assert_eq!(a[()], 0.); + a[()] = 1.; + assert_eq!(a[()], 1.); + assert_eq!(a.len(), 1); + assert_eq!(a.as_slice().unwrap(), &[1.]); +} + +#[test] +fn test_ix0_add() { + let mut a = Array::zeros(Ix0()); + a += 1.; + assert_eq!(a[()], 1.); + a += 2.; + assert_eq!(a[()], 3.); +} + +#[test] +fn test_ix0_add_add() { + let mut a = Array::zeros(Ix0()); + a += 1.; + let mut b = Array::zeros(Ix0()); + b += 1.; + a += &b; + assert_eq!(a[()], 2.); +} + +#[test] +fn test_ix0_add_broad() { + let mut b = Array::from_vec(vec![5., 6.]); + let mut a = Array::zeros(Ix0()); + a += 1.; + b += &a; + assert_eq!(b[0], 6.); + assert_eq!(b[1], 7.); +} From 58fa2c934c9b28d8386f7e8b979073d55410a20f Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Mar 2017 22:54:11 +0200 Subject: [PATCH 4/6] Add try_remove_axis to dimension --- src/dimension/dimension_trait.rs | 41 ++++++++++++++++++++++++++++++++ src/lib.rs | 14 +++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index 3d4b9fb6c..930fb7511 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -15,6 +15,7 @@ use itertools::{enumerate, zip}; use {Ix, Ixs, Ix0, Ix1, Ix2, Ix3, IxDyn, Dim, Si}; use IntoDimension; +use RemoveAxis; use {ArrayView1, ArrayViewMut1}; use {zipsl, zipsl_mut, ZipExt}; use Axis; @@ -64,6 +65,9 @@ pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default + /// - and so on.. /// - For `Vec`: `Vec`, type Pattern: IntoDimension; + // Next smaller dimension (if it exists) + #[doc(hidden)] + type TrySmaller: Dimension; #[doc(hidden)] fn ndim(&self) -> usize; @@ -341,6 +345,9 @@ pub unsafe trait Dimension : Clone + Eq + Debug + Send + Sync + Default + .max_by_key(|ax| ax.stride().abs()) .map_or(Axis(0), |ax| ax.axis()) } + + #[doc(hidden)] + fn try_remove_axis(&self, axis: Axis) -> Self::TrySmaller; } // utility functions @@ -361,6 +368,7 @@ fn abs_index(len: Ixs, index: Ixs) -> Ix { unsafe impl Dimension for Dim<[Ix; 0]> { type SliceArg = [Si; 0]; type Pattern = (); + type TrySmaller = Self; // empty product is 1 -> size is 1 #[inline] fn ndim(&self) -> usize { 0 } @@ -376,12 +384,17 @@ unsafe impl Dimension for Dim<[Ix; 0]> { fn next_for(&self, _index: Self) -> Option { None } + #[inline] + fn try_remove_axis(&self, _ignore: Axis) -> Self::TrySmaller { + *self + } } unsafe impl Dimension for Dim<[Ix; 1]> { type SliceArg = [Si; 1]; type Pattern = Ix; + type TrySmaller = ::Smaller; #[inline] fn ndim(&self) -> usize { 1 } #[inline] @@ -456,11 +469,16 @@ unsafe impl Dimension for Dim<[Ix; 1]> { None } } + #[inline] + fn try_remove_axis(&self, axis: Axis) -> Self::TrySmaller { + self.remove_axis(axis) + } } unsafe impl Dimension for Dim<[Ix; 2]> { type SliceArg = [Si; 2]; type Pattern = (Ix, Ix); + type TrySmaller = ::Smaller; #[inline] fn ndim(&self) -> usize { 2 } #[inline] @@ -601,11 +619,16 @@ unsafe impl Dimension for Dim<[Ix; 2]> { None } } + #[inline] + fn try_remove_axis(&self, axis: Axis) -> Self::TrySmaller { + self.remove_axis(axis) + } } unsafe impl Dimension for Dim<[Ix; 3]> { type SliceArg = [Si; 3]; type Pattern = (Ix, Ix, Ix); + type TrySmaller = ::Smaller; #[inline] fn ndim(&self) -> usize { 3 } #[inline] @@ -681,6 +704,10 @@ unsafe impl Dimension for Dim<[Ix; 3]> { } order } + #[inline] + fn try_remove_axis(&self, axis: Axis) -> Self::TrySmaller { + self.remove_axis(axis) + } } macro_rules! large_dim { @@ -688,6 +715,7 @@ macro_rules! large_dim { unsafe impl Dimension for Dim<[Ix; $n]> { type SliceArg = [Si; $n]; type Pattern = ($($ix,)*); + type TrySmaller = ::Smaller; #[inline] fn ndim(&self) -> usize { $n } #[inline] @@ -698,6 +726,10 @@ macro_rules! large_dim { fn slice(&self) -> &[Ix] { self.ix() } #[inline] fn slice_mut(&mut self) -> &mut [Ix] { self.ixm() } + #[inline] + fn try_remove_axis(&self, axis: Axis) -> Self::TrySmaller { + self.remove_axis(axis) + } } ) } @@ -712,6 +744,7 @@ unsafe impl Dimension for IxDyn { type SliceArg = [Si]; type Pattern = Self; + type TrySmaller = ::Smaller; #[inline] fn ndim(&self) -> usize { self.ix().len() } #[inline] @@ -722,6 +755,14 @@ unsafe impl Dimension for IxDyn fn into_pattern(self) -> Self::Pattern { self } + #[inline] + fn try_remove_axis(&self, axis: Axis) -> Self::TrySmaller { + if self.ndim() > 0 { + self.remove_axis(axis) + } else { + self.clone() + } + } } impl Index for Dim> diff --git a/src/lib.rs b/src/lib.rs index e6977e8dc..db1537654 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -606,6 +606,20 @@ impl ArrayBase row.into_iter_().fold((), |(), elt| f(elt)); } } + + /// Remove array axis `axis` and return the result. + fn try_remove_axis(self, axis: Axis) -> ArrayBase + { + let d = self.dim.try_remove_axis(axis); + let s = self.strides.try_remove_axis(axis); + ArrayBase { + ptr: self.ptr, + data: self.data, + dim: d, + strides: s, + } + } + } From 6f1c79b046baa3bb97fec3e8fe62777d5168d404 Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Mar 2017 22:54:11 +0200 Subject: [PATCH 5/6] Add dimension-reduced versions of InnerIter --- src/iterators/mod.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 8bd060168..e363219b3 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -520,6 +520,55 @@ impl<'a, A, D> ExactSizeIterator for InnerIterMut<'a, A, D> } } +/// Create an InnerIter one dimension smaller than D (if possible) +pub fn new_inner_iter_smaller(v: ArrayView) + -> InnerIter + where D: Dimension +{ + let ndim = v.ndim(); + let len; + let stride; + let iter_v; + if ndim == 0 { + len = 1; + stride = 0; + iter_v = v.try_remove_axis(Axis(0)) + } else { + len = v.dim.last_elem(); + stride = v.strides.last_elem() as isize; + iter_v = v.try_remove_axis(Axis(ndim - 1)) + } + InnerIter { + inner_len: len, + inner_stride: stride, + iter: iter_v.into_base_iter(), + } +} + +pub fn new_inner_iter_smaller_mut(v: ArrayViewMut) + -> InnerIterMut + where D: Dimension, +{ + let ndim = v.ndim(); + let len; + let stride; + let iter_v; + if ndim == 0 { + len = 1; + stride = 0; + iter_v = v.try_remove_axis(Axis(0)) + } else { + len = v.dim.last_elem(); + stride = v.strides.last_elem() as isize; + iter_v = v.try_remove_axis(Axis(ndim - 1)) + } + InnerIterMut { + inner_len: len, + inner_stride: stride, + iter: iter_v.into_base_iter(), + } +} + #[derive(Debug)] pub struct OuterIterCore { index: Ix, From da78e30de955963483eef436bf009912d0a1b7ef Mon Sep 17 00:00:00 2001 From: bluss Date: Wed, 29 Mar 2017 22:54:11 +0200 Subject: [PATCH 6/6] Use inner_iter_smaller in zip_mut_with This dimension-reduces the inner iterators before we use them in zip, which primarily reduces the code size for all the code outside the innermost loop. This should be a solid win by making the code size for all binary operations smaller. --- src/impl_methods.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 558150ce5..ca6e7f120 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -23,6 +23,10 @@ use super::ZipExt; use dimension::IntoDimension; use dimension::{axes_of, Axes, merge_axes, stride_offset}; use iterators::whole_chunks_of; +use iterators::{ + new_inner_iter_smaller, + new_inner_iter_smaller_mut, +}; use { NdIndex, @@ -1138,7 +1142,8 @@ impl ArrayBase where S: Data, D: Dimension } // otherwise, break the arrays up into their inner rows let mut try_slices = true; - let rows = self.inner_iter_mut().zip(rhs.inner_iter()); + let rows = new_inner_iter_smaller_mut(self.view_mut()).zip( + new_inner_iter_smaller(rhs.view())); for (mut s_row, r_row) in rows { if try_slices { if let Some(self_s) = s_row.as_slice_mut() {