I want a function to
Drop
, so I don't have to worry about freeing memoryThe obvious choice is Vec
, but how does it compare to a boxed slice on the heap? Vec
is more powerful, but I need the array for numerical math and, in my case, don't need stuff like push/pop. The idea is to have something with less features, but faster.
Below I have two versions of a "linspace" function (a la Matlab and numpy),
Vec
Both are used like
let y = linspace_*(start, stop, len);
where y
is a linearly spaced "array" (i.e. a Vec
in (1) and a boxed slice in (2)) of length len
.
For small "arrays" of length 1000, (1) is FASTER. For large arrays of length 4*10^6, (1) is SLOWER. Why is that? Am I doing something wrong in (2)?
When the argument len
= 1000, benchmarking by just calling the function results in
(1) ... bench: 879 ns/iter (+/- 12)
(2) ... bench: 1,295 ns/iter (+/- 38)
When the argument len
= 4000000, benchmarking results in
(1) ... bench: 5,802,836 ns/iter (+/- 90,209)
(2) ... bench: 4,767,234 ns/iter (+/- 121,596)
Listing of (1):
pub fn linspace_vec<'a, T: 'a>(start: T, stop: T, len: usize) -> Vec<T> where T: Float, { // get 0, 1 and the increment dx as T let (one, zero, dx) = get_values_as_type_t::<T>(start, stop, len); let mut v = vec![zero; len]; let mut c = zero; let ptr: *mut T = v.as_mut_ptr(); unsafe { for ii in 0..len { let x = ptr.offset((ii as isize)); *x = start + c * dx; c = c + one; } } return v; }
Listing of (2):
pub fn linspace_boxed_slice<'a, T: 'a>(start: T, stop: T, len: usize) -> Box<&'a mut [T]> where T: Float, { let (one, zero, dx) = get_values_as_type_t::<T>(start, stop, len); let size = len * mem::size_of::<T>(); unsafe { let ptr = heap::allocate(size, align_of::<T>()) as *mut T; let mut c = zero; for ii in 0..len { let x = ptr.offset((ii as isize)); *x = start + c * dx; c = c + one; } // IS THIS WHAT MAKES IT SLOW?: let sl = slice::from_raw_parts_mut(ptr, len); return Box::new(sl); } }
In your second version, you use the type Box<&'a mut [T]>
, which means there are two levels of indirection to reach a T
, because both Box
and &
are pointers.
What you want instead is a Box<[T]>
. I think the only sane way to construct such a value is from a Vec<T>
, using the into_boxed_slice
method. Note that the only benefit is that you lose the capacity
field that a Vec
would have. Unless you need to have a lot of these arrays in memory at the same time, the overhead is likely to be insignificant.
pub fn linspace_vec<'a, T: 'a>(start: T, stop: T, len: usize) -> Box<[T]> where T: Float, { // get 0, 1 and the increment dx as T let (one, zero, dx) = get_values_as_type_t::<T>(start, stop, len); let mut v = vec![zero; len].into_boxed_slice(); let mut c = zero; let ptr: *mut T = v.as_mut_ptr(); unsafe { for ii in 0..len { let x = ptr.offset((ii as isize)); *x = start + c * dx; c = c + one; } } v }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With