From 2a8b58a28980722a7198abb4d953fe1da3bcaa31 Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 14 May 2021 12:32:44 +0200 Subject: [PATCH 1/2] debloat: Factor out layout c/f check from layout computation Factor out the code that checks C/F memory layout into standalone functions. These functions will be more useful on their own. Also factor out the NdProducer Layout computation function, so that more of that code is shared between generic instatiations. Tested using cargo llvm-lines. --- src/dimension/mod.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ src/impl_methods.rs | 18 +--------------- src/zip/mod.rs | 48 +++++++++++++++++++++++------------------- 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 732006e2b..7bb92892c 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -668,6 +668,56 @@ pub fn slices_intersect( true } +pub(crate) fn is_layout_c(dim: &D, strides: &D) -> bool { + if let Some(1) = D::NDIM { + return strides[0] == 1 || dim[0] <= 1; + } + + for &d in dim.slice() { + if d == 0 { + return true; + } + } + + let mut contig_stride = 1_isize; + // check all dimensions -- a dimension of length 1 can have unequal strides + for (&dim, &s) in izip!(dim.slice().iter().rev(), strides.slice().iter().rev()) { + if dim != 1 { + let s = s as isize; + if s != contig_stride { + return false; + } + contig_stride *= dim as isize; + } + } + true +} + +pub(crate) fn is_layout_f(dim: &D, strides: &D) -> bool { + if let Some(1) = D::NDIM { + return strides[0] == 1 || dim[0] <= 1; + } + + for &d in dim.slice() { + if d == 0 { + return true; + } + } + + let mut contig_stride = 1_isize; + // check all dimensions -- a dimension of length 1 can have unequal strides + for (&dim, &s) in izip!(dim.slice(), strides.slice()) { + if dim != 1 { + let s = s as isize; + if s != contig_stride { + return false; + } + contig_stride *= dim as isize; + } + } + true +} + pub fn merge_axes(dim: &mut D, strides: &mut D, take: Axis, into: Axis) -> bool where D: Dimension, diff --git a/src/impl_methods.rs b/src/impl_methods.rs index c441efccd..d238f0245 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1382,23 +1382,7 @@ where /// Return `false` otherwise, i.e. the array is possibly not /// contiguous in memory, it has custom strides, etc. pub fn is_standard_layout(&self) -> bool { - fn is_standard_layout(dim: &D, strides: &D) -> bool { - if let Some(1) = D::NDIM { - return strides[0] == 1 || dim[0] <= 1; - } - if dim.slice().iter().any(|&d| d == 0) { - return true; - } - let defaults = dim.default_strides(); - // check all dimensions -- a dimension of length 1 can have unequal strides - for (&dim, &s, &ds) in izip!(dim.slice(), strides.slice(), defaults.slice()) { - if dim != 1 && s != ds { - return false; - } - } - true - } - is_standard_layout(&self.dim, &self.strides) + dimension::is_layout_c(&self.dim, &self.strides) } /// Return true if the array is known to be contiguous. diff --git a/src/zip/mod.rs b/src/zip/mod.rs index 7277f99f9..1ba0181c0 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs @@ -21,6 +21,7 @@ use crate::partial::Partial; use crate::indexes::{indices, Indices}; use crate::split_at::{SplitPreference, SplitAt}; +use crate::dimension; pub use self::ndproducer::{NdProducer, IntoNdProducer, Offset}; @@ -51,33 +52,38 @@ where private_decl! {} } +/// Compute `Layout` hints for array shape dim, strides +fn array_layout(dim: &D, strides: &D) -> Layout { + let n = dim.ndim(); + if dimension::is_layout_c(dim, strides) { + // effectively one-dimensional => C and F layout compatible + if n <= 1 || dim.slice().iter().filter(|&&len| len > 1).count() <= 1 { + Layout::one_dimensional() + } else { + Layout::c() + } + } else if n > 1 && dimension::is_layout_f(dim, strides) { + Layout::f() + } else if n > 1 { + if dim[0] > 1 && strides[0] == 1 { + Layout::fpref() + } else if dim[n - 1] > 1 && strides[n - 1] == 1 { + Layout::cpref() + } else { + Layout::none() + } + } else { + Layout::none() + } +} + impl ArrayBase where S: RawData, D: Dimension, { pub(crate) fn layout_impl(&self) -> Layout { - let n = self.ndim(); - if self.is_standard_layout() { - // effectively one-dimensional => C and F layout compatible - if n <= 1 || self.shape().iter().filter(|&&len| len > 1).count() <= 1 { - Layout::one_dimensional() - } else { - Layout::c() - } - } else if n > 1 && self.raw_view().reversed_axes().is_standard_layout() { - Layout::f() - } else if n > 1 { - if self.len_of(Axis(0)) > 1 && self.stride_of(Axis(0)) == 1 { - Layout::fpref() - } else if self.len_of(Axis(n - 1)) > 1 && self.stride_of(Axis(n - 1)) == 1 { - Layout::cpref() - } else { - Layout::none() - } - } else { - Layout::none() - } + array_layout(&self.dim, &self.strides) } } From 2ddc9dbd0234c1f4b03f759ed32fdf0fec8f6aaf Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 14 May 2021 13:07:40 +0200 Subject: [PATCH 2/2] debloat: Debloat pointer inbounds assert Debloat by pushing the code down into the DataReprs, this avoids instantiating the pointer check code for the views (where it can't do anything anyway). --- src/data_traits.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++ src/impl_methods.rs | 12 +--------- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/data_traits.rs b/src/data_traits.rs index 7d9f15b0a..bd540b1b8 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -33,8 +33,12 @@ pub unsafe trait RawData: Sized { #[doc(hidden)] // This method is only used for debugging + #[deprecated(note="Unused", since="0.15.2")] fn _data_slice(&self) -> Option<&[Self::Elem]>; + #[doc(hidden)] + fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool; + private_decl! {} } @@ -146,9 +150,15 @@ pub unsafe trait DataMut: Data + RawDataMut { unsafe impl RawData for RawViewRepr<*const A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -160,9 +170,15 @@ unsafe impl RawDataClone for RawViewRepr<*const A> { unsafe impl RawData for RawViewRepr<*mut A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -192,6 +208,11 @@ unsafe impl RawData for OwnedArcRepr { fn _data_slice(&self) -> Option<&[A]> { Some(self.0.as_slice()) } + + fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { + self.0._is_pointer_inbounds(self_ptr) + } + private_impl! {} } @@ -274,9 +295,18 @@ unsafe impl RawDataClone for OwnedArcRepr { unsafe impl RawData for OwnedRepr { type Elem = A; + fn _data_slice(&self) -> Option<&[A]> { Some(self.as_slice()) } + + fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool { + let slc = self.as_slice(); + let ptr = slc.as_ptr() as *mut A; + let end = unsafe { ptr.add(slc.len()) }; + self_ptr >= ptr && self_ptr <= end + } + private_impl! {} } @@ -340,9 +370,15 @@ where unsafe impl<'a, A> RawData for ViewRepr<&'a A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -364,9 +400,15 @@ unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> { unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> { type Elem = A; + + #[inline] fn _data_slice(&self) -> Option<&[A]> { None } + + #[inline(always)] + fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true } + private_impl! {} } @@ -458,12 +500,23 @@ unsafe impl DataOwned for OwnedArcRepr { unsafe impl<'a, A> RawData for CowRepr<'a, A> { type Elem = A; + fn _data_slice(&self) -> Option<&[A]> { + #[allow(deprecated)] match self { CowRepr::View(view) => view._data_slice(), CowRepr::Owned(data) => data._data_slice(), } } + + #[inline] + fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool { + match self { + CowRepr::View(view) => view._is_pointer_inbounds(ptr), + CowRepr::Owned(data) => data._is_pointer_inbounds(ptr), + } + } + private_impl! {} } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index d238f0245..21470ec0f 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2135,17 +2135,7 @@ where } pub(crate) fn pointer_is_inbounds(&self) -> bool { - match self.data._data_slice() { - None => { - // special case for non-owned views - true - } - Some(slc) => { - let ptr = slc.as_ptr() as *mut A; - let end = unsafe { ptr.add(slc.len()) }; - self.ptr.as_ptr() >= ptr && self.ptr.as_ptr() <= end - } - } + self.data._is_pointer_inbounds(self.as_ptr()) } /// Perform an elementwise assigment to `self` from `rhs`.