Repository: bluss/rust-ndarray
Pull request #295 (2017-04-01 21:40:01)

Minor fixes and cleanups and speedups and code size improvements
- WholeChunksIter/Mut is now Send/Sync as apporpriate
- Otherwise small refactoring and fixups


Patch:
diff --git a/benches/bench1.rs b/benches/bench1.rs
index a7efd92d..b765486a 100644
--- a/benches/bench1.rs
+++ b/benches/bench1.rs
@@ -14,6 +14,7 @@ use ndarray::{
     Zip,
 };
 use ndarray::{arr0, arr1, arr2};
+use ndarray::ShapeBuilder;
 
 use test::black_box;
 
@@ -214,6 +215,17 @@ fn scalar_sum_2d_float_cutout(bench: &mut test::Bencher)
     });
 }
 
+#[bench]
+fn scalar_sum_2d_float_t_cutout(bench: &mut test::Bencher)
+{
+    let a = Array::<f32, _>::zeros((66, 66));
+    let av = a.slice(s![1..-1, 1..-1]).reversed_axes();
+    let a = black_box(av);
+    bench.iter(|| {
+        a.scalar_sum()
+    });
+}
+
 #[bench]
 fn fold_sum_i32_2d_regular(bench: &mut test::Bencher)
 {
@@ -808,6 +820,14 @@ fn equality_f32(bench: &mut test::Bencher)
     bench.iter(|| a == b);
 }
 
+#[bench]
+fn equality_f32_mixorder(bench: &mut test::Bencher)
+{
+    let a = Array::<f32, _>::zeros((64, 64));
+    let b = Array::<f32, _>::zeros((64, 64).f());
+    bench.iter(|| a == b);
+}
+
 #[bench]
 fn dot_f32_16(bench: &mut test::Bencher)
 {
diff --git a/benches/higher-order.rs b/benches/higher-order.rs
index 7f2aa920..c249c71b 100644
--- a/benches/higher-order.rs
+++ b/benches/higher-order.rs
@@ -3,6 +3,7 @@
 
 extern crate test;
 use test::Bencher;
+use test::black_box;
 
 #[macro_use(s)]
 extern crate ndarray;
@@ -66,3 +67,24 @@ fn fold_axis(bench: &mut Bencher)
         a.fold_axis(Axis(0), 0., |&acc, &elt| acc + elt)
     });
 }
+
+const MA: usize = 64;
+const MASZ: usize = MA * MA;
+
+#[bench]
+fn map_axis_0(bench: &mut Bencher)
+{
+    let a = Array::from_iter(0..MASZ as i32).into_shape([MA, MA]).unwrap();
+    bench.iter(|| {
+        a.map_axis(Axis(0), |lane| black_box(lane))
+    });
+}
+
+#[bench]
+fn map_axis_1(bench: &mut Bencher)
+{
+    let a = Array::from_iter(0..MASZ as i32).into_shape([MA, MA]).unwrap();
+    bench.iter(|| {
+        a.map_axis(Axis(1), |lane| black_box(lane))
+    });
+}
diff --git a/src/arraytraits.rs b/src/arraytraits.rs
index e165f898..d74af9e8 100644
--- a/src/arraytraits.rs
+++ b/src/arraytraits.rs
@@ -22,6 +22,7 @@ use {
 };
 
 use numeric_util;
+use {Zip, FoldWhile};
 
 #[cold]
 #[inline(never)]
@@ -102,7 +103,14 @@ impl<S, S2, D> PartialEq<ArrayBase<S2, D>> for ArrayBase<S, D>
                 return numeric_util::unrolled_eq(self_s, rhs_s);
             }
         }
-        self.iter().zip(rhs.iter()).all(|(a, b)| a == b)
+        Zip::from(self)
+            .and(rhs)
+            .fold_while(true, |_, a, b|
+            if a != b {
+                FoldWhile::Done(false)
+            } else {
+                FoldWhile::Continue(true)
+            }).into_inner()
     }
 }
 
@@ -173,12 +181,13 @@ impl<'a, S, D> hash::Hash for ArrayBase<S, D>
           S: Data,
           S::Elem: hash::Hash
 {
+    // Note: elements are hashed in the logical order
     fn hash<H: hash::Hasher>(&self, state: &mut H) {
         self.shape().hash(state);
         if let Some(self_s) = self.as_slice() {
             hash::Hash::hash_slice(self_s, state);
         } else {
-            for row in self.inner_iter() {
+            for row in self.inner_rows() {
                 if let Some(row_s) = row.as_slice() {
                     hash::Hash::hash_slice(row_s, state);
                 } else {
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index b7f9f511..eec5eaba 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -339,25 +339,6 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
         }
     }
 
-    // `uget` for one-dimensional arrays
-    unsafe fn uget_1d(&self, i: Ix) -> &A {
-        debug_assert!(self.ndim() <= 1);
-        debug_assert!(i < self.len());
-        let offset = self.strides()[0] * (i as Ixs);
-        &*self.as_ptr().offset(offset)
-    }
-
-    // `uget_mut` for one-dimensional arrays
-    unsafe fn uget_mut_1d(&mut self, i: Ix) -> &mut A
-        where S: DataMut
-    {
-        debug_assert!(self.ndim() <= 1);
-        debug_assert!(i < self.len());
-        let offset = self.strides()[0] * (i as Ixs);
-        &mut *self.as_mut_ptr().offset(offset)
-    }
-
-
     /// Along `axis`, select the subview `index` and return a
     /// view with that axis removed.
     ///
@@ -1177,17 +1158,7 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
         debug_assert_eq!(self.shape(), rhs.shape());
         debug_assert_ne!(self.ndim(), 0);
 
-        // The one dimensional case is simple; we know they are not contig
-        if self.ndim() == 1 {
-            unsafe {
-                for i in 0..self.len() {
-                    f(self.uget_mut_1d(i), rhs.uget_1d(i));
-                }
-            }
-            return;
-        }
-
-        // otherwise, break the arrays up into their inner rows
+        // break the arrays up into their inner rows
         let n = self.ndim();
         let dim = self.raw_dim();
         Zip::from(new_inners_mut(self.view_mut(), Axis(n - 1)))
@@ -1276,13 +1247,8 @@ impl<A, S, D> ArrayBase<S, D> where S: Data<Elem=A>, D: Dimension
               A: 'a,
     {
         if let Some(slc) = self.as_slice_memory_order() {
-            // FIXME: Why is this (= indexed loop) optimizing the best?
-            let mut v = Vec::with_capacity(slc.len());
+            let v = ::iterators::to_vec_mapped(slc.iter(), f);
             unsafe {
-                for i in 0..slc.len() {
-                    *v.get_unchecked_mut(i) = f(&slc[i]);
-                    v.set_len(i + 1);
-                }
                 ArrayBase::from_shape_vec_unchecked(
                     self.dim.clone().strides(self.strides.clone()), v)
             }
diff --git a/src/impl_views.rs b/src/impl_views.rs
index ac2f787c..a3a185da 100644
--- a/src/impl_views.rs
+++ b/src/impl_views.rs
@@ -228,15 +228,8 @@ impl<'a, A, D> ArrayBase<ViewRepr<&'a mut A>, D>
 
     /// Return the array’s data as a slice, if it is contiguous and in standard order.
     /// Return `None` otherwise.
-    pub fn into_slice(self) -> Option<&'a mut [A]>
-    {
-        if self.is_standard_layout() {
-            unsafe {
-                Some(slice::from_raw_parts_mut(self.ptr, self.len()))
-            }
-        } else {
-            None
-        }
+    pub fn into_slice(self) -> Option<&'a mut [A]> {
+        self.into_slice_().ok()
     }
 
 }
diff --git a/src/iterators/inners.rs b/src/iterators/inners.rs
index fd1fceed..b8dd4e4b 100644
--- a/src/iterators/inners.rs
+++ b/src/iterators/inners.rs
@@ -1,6 +1,8 @@
 
 use imp_prelude::*;
 use {NdProducer, Layout};
+use super::InnerIter;
+use super::InnerIterMut;
 
 impl_ndproducer! {
     ['a, A, D: Dimension]
@@ -70,6 +72,20 @@ impl_ndproducer! {
     }
 }
 
+impl<'a, A, D> IntoIterator for Inners<'a, A, D>
+    where D: Dimension,
+{
+    type Item = <Self::IntoIter as Iterator>::Item;
+    type IntoIter = InnerIter<'a, A, D>;
+    fn into_iter(self) -> Self::IntoIter {
+        InnerIter {
+            iter: self.base.into_base_iter(),
+            inner_len: self.inner_len,
+            inner_stride: self.inner_stride,
+        }
+    }
+}
+
 pub struct InnersMut<'a, A: 'a, D> {
     base: ArrayViewMut<'a, A, D>,
     inner_len: Ix,
@@ -101,3 +117,17 @@ pub fn new_inners_mut<A, D>(v: ArrayViewMut<A, D>, axis: Axis)
         base: iter_v,
     }
 }
+
+impl<'a, A, D> IntoIterator for InnersMut<'a, A, D>
+    where D: Dimension,
+{
+    type Item = <Self::IntoIter as Iterator>::Item;
+    type IntoIter = InnerIterMut<'a, A, D>;
+    fn into_iter(self) -> Self::IntoIter {
+        InnerIterMut {
+            iter: self.base.into_base_iter(),
+            inner_len: self.inner_len,
+            inner_stride: self.inner_stride,
+        }
+    }
+}
diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs
index 5bb2745c..ff69b113 100644
--- a/src/iterators/mod.rs
+++ b/src/iterators/mod.rs
@@ -39,6 +39,8 @@ pub use self::chunks::{
 pub use self::inners::{
     new_inners,
     new_inners_mut,
+    Inners,
+    InnersMut,
 };
 
 /// Base for array iterators
@@ -1154,12 +1156,14 @@ send_sync_read_only!(IndexedIter);
 send_sync_read_only!(InnerIter);
 send_sync_read_only!(AxisIter);
 send_sync_read_only!(AxisChunksIter);
+send_sync_read_only!(ElementsBase);
 
 send_sync_read_write!(IterMut);
 send_sync_read_write!(IndexedIterMut);
 send_sync_read_write!(InnerIterMut);
 send_sync_read_write!(AxisIterMut);
 send_sync_read_write!(AxisChunksIterMut);
+send_sync_read_write!(ElementsBaseMut);
 
 /// (Trait used internally) An iterator that we trust
 /// to deliver exactly as many items as it said it would.
diff --git a/src/lib.rs b/src/lib.rs
index 0782ab8d..c58b47f4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -623,7 +623,7 @@ impl<A, S, D> ArrayBase<S, D>
             }
             return;
         }
-        for row in self.inner_iter_mut() {
+        for row in self.inner_rows_mut() {
             row.into_iter_().fold((), |(), elt| f(elt));
         }
     }
@@ -641,6 +641,20 @@ impl<A, S, D> ArrayBase<S, D>
         }
     }
 
+    /// n-d generalization of rows, just like inner iter
+    fn inner_rows(&self) -> iterators::Inners<A, D::TrySmaller>
+    {
+        let n = self.ndim();
+        iterators::new_inners(self.view(), Axis(n.saturating_sub(1)))
+    }
+
+    /// n-d generalization of rows, just like inner iter
+    fn inner_rows_mut(&mut self) -> iterators::InnersMut<A, D::TrySmaller>
+        where S: DataMut
+    {
+        let n = self.ndim();
+        iterators::new_inners_mut(self.view_mut(), Axis(n.saturating_sub(1)))
+    }
 }
 
 
@@ -742,17 +756,23 @@ impl<'a, A, D> ArrayBase<ViewRepr<&'a mut A>, D>
         ElementsBaseMut { inner: self.into_base_iter() }
     }
 
+    fn into_slice_(self) -> Result<&'a mut [A], Self> {
+        if self.is_standard_layout() {
+            unsafe {
+                Ok(slice::from_raw_parts_mut(self.ptr, self.len()))
+            }
+        } else {
+            Err(self)
+        }
+    }
+
     fn into_iter_(self) -> IterMut<'a, A, D> {
         IterMut {
             inner:
-                if self.is_standard_layout() {
-                    let slc = unsafe {
-                        slice::from_raw_parts_mut(self.ptr, self.len())
-                    };
-                    ElementsRepr::Slice(slc.iter_mut())
-                } else {
-                    ElementsRepr::Counted(self.into_elements_base())
-                }
+            match self.into_slice_() {
+                Ok(x) => ElementsRepr::Slice(x.into_iter()),
+                Err(self_) => ElementsRepr::Counted(self_.into_elements_base()),
+            }
         }
     }
 
diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs
index b5260937..8beeeba0 100644
--- a/src/numeric/impl_numeric.rs
+++ b/src/numeric/impl_numeric.rs
@@ -38,7 +38,7 @@ impl<A, S, D> ArrayBase<S, D>
             return numeric_util::unrolled_sum(slc);
         }
         let mut sum = A::zero();
-        for row in self.inner_iter() {
+        for row in self.inner_rows() {
             if let Some(slc) = row.as_slice() {
                 sum = sum + numeric_util::unrolled_sum(slc);
             } else {


Test patch:
diff --git a/tests/iterators.rs b/tests/iterators.rs
index 104ef775..9c9d58c4 100644
--- a/tests/iterators.rs
+++ b/tests/iterators.rs
@@ -480,6 +480,10 @@ fn iterators_are_send_sync() {
     _send_sync(&a.axis_chunks_iter(Axis(1), 1));
     _send_sync(&a.axis_chunks_iter_mut(Axis(1), 1));
     _send_sync(&indices(a.dim()));
+    _send_sync(&a.whole_chunks((1, 1, 1)));
+    _send_sync(&a.whole_chunks_mut((1, 1, 1)));
+    _send_sync(&a.whole_chunks((1, 1, 1)).into_iter());
+    _send_sync(&a.whole_chunks_mut((1, 1, 1)).into_iter());
 }
 
 #[test]