如何在Rust中提高逐元素乘法的性能?

6

我将对拥有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
}

3
这个怎么样:v1.iter().zip(v2).map(|(&i1, &i2)| i1 * i2).collect() - Francis Gagné
考虑向量化操作。也许使用线性代数或数字数组库会有益处。 - Mateen Ulhaq
@MateenUlhaq Vec::with_capacity 预先分配足够的内存;这里不应该重新调整 Vec 的大小。Iterator::collect 也使用 Vec::with_capacity - Francis Gagné
@Francis Gagné 那个有效!谢谢! - ryn1x
1个回答

9
当您在Vec或切片上使用索引器运算符时,编译器必须检查索引是否越界。
但是,当您使用迭代器时,这些边界检查被省略了,因为迭代器已经被精心编写,以确保它们永远不会读取超出边界的数据。此外,由于Rust中借用的工作方式,当迭代器存在于数据结构之上时,不可能通过其他方式对该数据结构进行修改(除非通过该迭代器本身),因此在迭代过程中有效范围不可能发生变化。
由于您同时迭代两个不同的数据结构,因此您将需要使用 zip 迭代适配器。 zip 在其中一个迭代器耗尽时停止,因此验证这两个向量具有相同长度仍然很重要。 zip 生成一个元组的迭代器,其中每个元组包含两个原始迭代器中相同位置的项目。然后,您可以使用 map 将每个元组转换为两个值的乘积。最后,您需要将由 map 生成的新迭代器 collect 到一个 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()
}

注意:我已将签名更改为使用切片而不是向量的引用。有关更多信息,请参见 为什么不建议接受对String(&String),Vec(&Vec)或Box(&Box)的引用作为函数参数?

1
zip_eq - Stargateur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接