Vec和盒装切片的性能比较

poi*_*idl 12 performance rust

我想要一个功能

  • 在堆上分配一个基本的可变长度"数组"(在一般意义上的单词,不一定是Rust类型)浮点数
  • 用值初始化它
  • 实现Drop,所以我不必担心释放内存
  • 实现索引或迭代的东西

显而易见的选择是Vec,但它与堆上的盒装切片相比如何?Vec更强大,但我需要数组数学,在我的情况下,不需要像push/pop这样的东西.我们的想法是拥有功能较少但速度更快的东西.

下面我有两个版本的"linspace"函数(la Matlab和numpy),

  1. "linspace_vec"(见下面的清单)使用 Vec
  2. "linspace_boxed_slice"(参见下面的清单)使用盒装切片

两者都用得像

let y = linspace_*(start, stop, len);
Run Code Online (Sandbox Code Playgroud)

其中y是长度为线性间隔的"阵列"(即Vec(1)中的(1)和(2)中的盒装切片)len.

对于长度为1000的小"阵列",(1)更快.对于长度为4*10 ^ 6的大型阵列,(1)为SLOWER.这是为什么?我在(2)做错了吗?

当参数len= 1000时,仅通过调用函数进行基准测试就会产生

  • (1) ... bench: 879 ns/iter (+/- 12)
  • (2) ... bench: 1,295 ns/iter (+/- 38)

当参数len= 4000000时,基准测试结果为

  • (1) ... bench: 5,802,836 ns/iter (+/- 90,209)
  • (2) ... bench: 4,767,234 ns/iter (+/- 121,596)

(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;
}
Run Code Online (Sandbox Code Playgroud)

(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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Fra*_*gné 21

在你的第二个版本,您使用的类型Box<&'a mut [T]>,这意味着有两个层次间接达成T,因为这两个Box&的指针.

你想要的是一个Box<[T]>.我认为构建这样一个值的唯一合理方法是Vec<T>使用该into_boxed_slice方法.请注意,唯一的好处是你会失去capacity一个Vec可能拥有的领域.除非您需要在内存中同时拥有大量这些数组,否则开销可能无关紧要.

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
}
Run Code Online (Sandbox Code Playgroud)

  • 除了使用 `Vec` 之外,似乎没有一种方法可以直接在语言或 Rust 1.4.0 的标准库中分配可变长度数组。如果你想避免创建一个`Vec`,请在[源代码](https://github.com /rust-lang/rust),然后在你的程序/库中复制代码,只保留必要的部分。 (3认同)
  • 哇,你们回复得很快:)谢谢! (2认同)
  • 还有一种方法可以从装箱的固定大小的数组(“ Box &lt;[T; N]&gt;”)中创建“ Box &lt;[T]&gt;”,可以对其进行强制转换或强制转换。 (2认同)