【问题标题】:How can I improve the performance of element-wise multiplication in Rust?如何提高 Rust 中逐元素乘法的性能?
【发布时间】:2019-02-10 01:02:48
【问题描述】:

我将对具有 10^6+ 个元素的多个向量进行逐元素乘法。这在分析中被标记为我的代码中最慢的部分之一,那么我该如何改进它?

/// element-wise multiplication for vecs
pub fn vec_mul<T>(v1: &Vec<T>, v2: &Vec<T>) -> Vec<T>
where
    T: std::ops::Mul<Output = T> + Copy,
{
    if v1.len() != v2.len() {
        panic!("Cannot multiply vectors of different lengths!")
    }
    let mut out: Vec<T> = Vec::with_capacity(v1.len());
    for i in 0..(v1.len()) {
        out.push(v1[i] * v2[i]);
    }
    out
}

【问题讨论】:

  • 这个怎么样:v1.iter().zip(v2).map(|(&amp;i1, &amp;i2)| i1 * i2).collect()?
  • 考虑矢量化操作。也许使用线性代数或数值数组库可能会有所帮助。 您还通过多次调用.push 来不必要地动态调整Vec 的大小。
  • @MateenUlhaq Vec::with_capacity 预分配足够的内存; Vec 永远不必在这里调整大小。 Iterator::collect 也使用 Vec::with_capacity
  • @Francis Gagné 成功了!谢谢!

标签: performance optimization rust


【解决方案1】:

当您在Vec 或切片上使用索引器运算符时,编译器必须检查索引是在边界内还是在边界外。

但是,当您使用迭代器时,这些边界检查将被忽略,因为迭代器已经过仔细编写,以确保它们不会越界读取。此外,由于 Rust 中借用的工作方式,当迭代器存在于该数据结构上时(通过该迭代器本身除外),该数据结构不能被改变,因此在迭代期间不可能改变有效边界。

由于您同时迭代两种不同的数据结构,因此您需要使用zip 迭代器适配器。 zip 在一个迭代器用完后立即停止,因此验证两个向量是否具有相同的长度仍然很重要。 zip 产生一个元组迭代器,其中每个元组包含两个原始迭代器中相同位置的项。然后您可以使用map 将每个元组转换为两个值的乘积。最后,您需要将collect 生成的新迭代器map 转换为Vec,然后您可以从您的函数中返回它。 collect 使用 size_hint 为使用 Vec::with_capacity 的向量预分配内存。

/// element-wise multiplication for vecs
pub fn vec_mul<T>(v1: &[T], v2: &[T]) -> Vec<T>
where
    T: std::ops::Mul<Output = T> + Copy,
{
    if v1.len() != v2.len() {
        panic!("Cannot multiply vectors of different lengths!")
    }

    v1.iter().zip(v2).map(|(&i1, &i2)| i1 * i2).collect()
}

注意:我已将签名更改为采用切片而不是对向量的引用。请参阅Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument? 了解更多信息。

【讨论】:

猜你喜欢
  • 2017-11-06
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
  • 2018-10-23
  • 2015-01-08
  • 2014-11-13
  • 2018-12-20
  • 1970-01-01
相关资源
最近更新 更多