From ed8dbcb5d1523cc7a2ec595bc9ffe6edcb78a930 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 7 Sep 2017 21:57:57 +0200 Subject: [PATCH 1/3] FEAT: Add bounds check elimination example file --- examples/bounds_check_elim.rs | 103 ++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 examples/bounds_check_elim.rs diff --git a/examples/bounds_check_elim.rs b/examples/bounds_check_elim.rs new file mode 100644 index 000000000..dc9e1e22a --- /dev/null +++ b/examples/bounds_check_elim.rs @@ -0,0 +1,103 @@ +#![crate_type="lib"] + +// Test cases for bounds check elimination + +extern crate ndarray; + +use ndarray::prelude::*; + +/* +pub fn testslice(a: &[f64]) -> f64 { + let mut sum = 0.; + for i in 0..a.len() { + sum += a[i]; + } + sum +} + +pub fn testvec(a: &Vec) -> f64 { + let mut sum = 0.; + for i in 0..a.len() { + sum += a[i]; + } + sum +} + +pub fn testvec_as_slice(a: &Vec) -> f64 { + let a = a.as_slice(); + let mut sum = 0.; + for i in 0..a.len() { + sum += a[i]; + } + sum +} +*/ + +#[no_mangle] +pub fn test1d_single(a: &Array1, i: usize) -> f64 { + if i < a.len() { a[i] } else { 0. } +} + +#[no_mangle] +pub fn test1d_single_mut(a: &mut Array1, i: usize) -> f64 { + if i < a.len() { *&mut a[i] } else { 0. } +} + +#[no_mangle] +pub fn test1d_len_of(a: &Array1) -> f64 { + let a = &*a; + let mut sum = 0.; + for i in 0..a.len_of(Axis(0)) { + sum += a[i]; + } + sum +} + +#[no_mangle] +pub fn test1d_range(a: &Array1) -> f64 { + let mut sum = 0.; + for i in 0..a.len() { + sum += a[i]; + } + sum +} + +#[no_mangle] +pub fn test1d_while(a: &Array1) -> f64 { + let mut sum = 0.; + let mut i = 0; + while i < a.len() { + sum += a[i]; + i += 1; + } + sum +} + +#[no_mangle] +pub fn test2d_ranges(a: &Array2) -> f64 { + let mut sum = 0.; + for i in 0..a.rows() { + for j in 0..a.cols() { + sum += a[[i, j]]; + } + } + sum +} + +#[no_mangle] +pub fn test2d_whiles(a: &Array2) -> f64 { + let mut sum = 0.; + let mut i = 0; + while i < a.rows() { + let mut j = 0; + while j < a.cols() { + sum += a[[i, j]]; + j += 1; + } + i += 1; + } + sum +} + +fn main() { +} From 325bcf338767dc657f11d7dc481a8da0f953db98 Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 7 Sep 2017 22:01:29 +0200 Subject: [PATCH 2/3] FEAT: Tweak bounds checked indexing code (Index, IndexMut) Tweak bounds checked indexing code so that the compiler understands to remove the bounds check in some simple cases. See examples in the new example file bounds_check_elim.rs This helps in *some* cases where a conditional or loop conditional already do the required bounds check. Simple examples are if i < len and while i < len loops; the range iterator loop is a worse example, it doesn't do this as well! Further works is needed on that. Input code: ```rust pub fn test1d_single(a: &Array1, i: usize) -> f64 { if i < a.len() { a[i] } else { 0. } } ``` Codegen diff: ```diff test1d_single: .cfi_startproc - push rax -.Lcfi0: - .cfi_def_cfa_offset 16 xorps xmm0, xmm0 cmp qword ptr [rdi + 32], rsi - jbe .LBB0_3 + jbe .LBB0_2 + mov rax, qword ptr [rdi + 24] imul rsi, qword ptr [rdi + 40] - shl rsi, 3 - add rsi, qword ptr [rdi + 24] - je .LBB0_4 - movsd xmm0, qword ptr [rsi] -.LBB0_3: - pop rax + movsd xmm0, qword ptr [rax + 8*rsi] +.LBB0_2: ret -.LBB0_4: - call _ZN7ndarray11arraytraits19array_out_of_bounds17h009d4a482e88d6aaE@PLT .Lfunc_end0: .size test1d_single, .Lfunc_end0-test1d_single .cfi_endproc ``` --- src/arraytraits.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 4a8d209cb..d7f5710c0 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -69,7 +69,10 @@ impl Index for ArrayBase #[inline] fn index(&self, index: I) -> &S::Elem { debug_bounds_check!(self, index); - self.get(index).unwrap_or_else(|| array_out_of_bounds()) + unsafe { + &*self.ptr.offset(index.index_checked(&self.dim, &self.strides) + .unwrap_or_else(|| array_out_of_bounds())) + } } } @@ -84,7 +87,10 @@ impl IndexMut for ArrayBase #[inline] fn index_mut(&mut self, index: I) -> &mut S::Elem { debug_bounds_check!(self, index); - self.get_mut(index).unwrap_or_else(|| array_out_of_bounds()) + unsafe { + &mut *self.as_mut_ptr().offset(index.index_checked(&self.dim, &self.strides) + .unwrap_or_else(|| array_out_of_bounds())) + } } } From 4f5c305b5acf2cfb04586cabc3040538865739bb Mon Sep 17 00:00:00 2001 From: bluss Date: Thu, 7 Sep 2017 22:44:32 +0200 Subject: [PATCH 3/3] MAINT: Add tricky copy on write test Test cow with an array that changes strides when it is unsharing itself. --- tests/array.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/array.rs b/tests/array.rs index 85bae74b1..1b1981a8c 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -219,6 +219,41 @@ fn test_cow() assert_eq!(before[3], 1); } +#[test] +fn test_cow_shrink() +{ + // A test for clone-on-write in the case that + // mutation shrinks the array and gives it different strides + // + let mut mat = RcArray::zeros((2, 3)); + //mat.islice(s![.., ..;2]); + mat[[0, 0]] = 1; + let n = mat.clone(); + mat[[0, 1]] = 2; + mat[[0, 2]] = 3; + mat[[1, 0]] = 4; + mat[[1, 1]] = 5; + mat[[1, 2]] = 6; + assert_eq!(mat[[0, 0]], 1); + assert_eq!(mat[[0, 1]], 2); + assert_eq!(n[[0, 0]], 1); + assert_eq!(n[[0, 1]], 0); + assert_eq!(n.get((0, 1)), Some(&0)); + // small has non-C strides this way + let mut small = mat.reshape(6); + small.islice(s![4..;-1]); + assert_eq!(small[0], 6); + assert_eq!(small[1], 5); + let before = small.clone(); + // mutation + // small gets back C strides in CoW. + small[1] = 9; + assert_eq!(small[0], 6); + assert_eq!(small[1], 9); + assert_eq!(before[0], 6); + assert_eq!(before[1], 5); +} + #[test] fn test_sub() {