简体   繁体   English

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

[英]How can I improve the performance of element-wise multiplication in Rust?

I will be doing element-wise multiplication on multiple vectors with 10^6+ elements. 我将使用10 ^ 6 +元素对多个向量进行元素乘法。 This is being flagged in profiling as one of the slowest parts of my code, so how can I improve it? 这在标题中被标记为我的代码中最慢的部分之一,所以我该如何改进它?

/// 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
}

When you use the indexer operator on a Vec or a slice, the compiler has to check whether the index is in bounds or out of bounds. Vec或切片上使用索引器运算符时,编译器必须检查索引是在边界内还是在边界外。

However, when you use iterators, these bounds checks are omitted, because the iterators have been carefully written to ensure that they never read out of bounds. 但是,当您使用迭代器时,这些边界检查将被省略,因为迭代器已经过仔细编写,以确保它们永远不会读出边界。 Furthermore, due to how borrowing works in Rust, a data structure cannot be mutated while an iterator exists over that data structure (except via that iterator itself), so it's impossible for the valid bounds to change during iteration. 此外,由于借用如何在Rust中工作,当迭代器存在于该数据结构上时(通过迭代器本身除外),数据结构不能被变异,因此在迭代期间有效边界不可能发生变化。

Since you are iterating over two different data structures concurrently, you'll want to use the zip iterator adapter. 由于您同时迭代两个不同的数据结构,因此您将需要使用zip迭代器适配器。 zip stops as soon as one iterator is exhausted, so it's still relevant to validate that both vectors have the same length. 只要一个迭代器耗尽, zip停止,因此仍然需要验证两个向量是否具有相同的长度。 zip produces an iterator of tuples, where each tuple contains the items at the same position in the two original iterators. zip生成元组的迭代器,其中每个元组包含两个原始迭代器中相同位置的项。 Then you can use map to transform each tuple into the product of the two values. 然后,您可以使用map将每个元组转换为两个值的乘积。 Finally, you'll want to collect the new iterator produced by map into a Vec which you can then return from your function. 最后,您需要将map生成的新迭代器collectVec ,然后可以从函数返回。 collect uses size_hint to preallocate memory for the vector using Vec::with_capacity . 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()
}

Note: I've changed the signature to take slices instead of references to vectors. 注意:我已经更改了签名以获取切片而不是对向量的引用。 See Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument? 请参阅为什么不鼓励接受对String(&String),Vec(&Vec)或Box(&Box)的引用作为函数参数? for more information. 欲获得更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM