【问题标题】:Performance comparison of a Vec and a boxed sliceVec 和盒装切片的性能比较
【发布时间】:2016-02-15 17:37:07
【问题描述】:

我想要一个函数来

  • 在堆上分配一个基本的可变长度“数组”(一般意义上,不一定是 Rust 类型)的浮点数
  • 用值初始化它
  • 实现Drop,这样我就不用担心释放内存了
  • 为索引或迭代实现一些东西

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

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

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

两者都像

let y = linspace_*(start, stop, len);

其中y 是长度为len 的线性间隔“数组”(即(1)中的Vec 和(2)中的盒装切片)。

对于长度为 1000 的小型“数组”,(1) 更快。对于长度为 4*10^6 的大型数组,(1) 更慢。这是为什么?我在 (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;
}

(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);
    }
}

【问题讨论】:

标签: performance rust


【解决方案1】:

在您的第二个版本中,您使用Box&lt;&amp;'a mut [T]&gt; 类型,这意味着有两个间接级别可以到达T,因为Box&amp; 都是指针。

您想要的是Box&lt;[T]&gt;。我认为构造这样一个值的唯一明智的方法是使用Vec&lt;T&gt; 方法,使用into_boxed_slice 方法。请注意,唯一的好处是您丢失了Vec 将拥有的capacity 字段。除非您需要同时在内存中拥有大量此类数组,否则开销可能微不足道。

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
}

【讨论】:

  • 哇,你们回复得很快:)谢谢!
  • 还有另一种方法来创建Box&lt;[T]&gt;,从一个固定大小的盒装数组 (Box&lt;[T; N]&gt;),它可以被强制转换或者它会简单地强制。
  • @bluss: 但我可以用 VARIABLE N 制作一个 [T;N](在我装箱之前)吗?
  • 除了使用 Vec 之外,似乎没有办法直接在语言或 Rust 1.4.0 的标准库中分配可变长度数组。如果您想避免创建Vec,请在the source code 中查找它如何分配可变长度数组以及如何将其转换为Box&lt;[T]&gt;,然后将代码复制到您的程序/库中,仅保留部分这是必要的。
  • 对于可变长度的盒子切片,现在有一个只在夜间使用的实验性 API Box::<T>::new_uninit_slice(len: usize)。我尝试使用它来减少 WASM 二进制大小以获得可悲的结果:8624B 到 8317B...
猜你喜欢
  • 2016-03-22
  • 2011-09-06
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 2021-08-31
  • 2018-10-31
  • 1970-01-01
  • 2012-03-29
相关资源
最近更新 更多