如何提高Rust中按元素乘法的性能? [英] How can I improve the performance of element-wise multiplication in Rust?

查看:103
本文介绍了如何提高Rust中按元素乘法的性能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I will be doing element-wise multiplication on multiple vectors with 10^6+ elements. 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
}

推荐答案

Vec或切片上使用索引器运算符时,编译器必须检查索引是在范围内还是在范围之外.

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.

但是,当您使用迭代器时,将省略这些边界检查,因为已经精心编写了迭代器以确保它们永远不会超出边界.此外,由于借用在Rust中是如何工作的,因此当迭代器存在于该数据结构上时,该数据结构将无法更改(通过该迭代器本身除外),因此在迭代过程中有效边界是不可能更改的.

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.

由于同时要遍历两个不同的数据结构,因此需要使用 map 将每个元组转换为两个值的乘积.最后,您需要 collect map生成为Vec的新迭代器,然后可以从函数中返回它. collect使用 size_hint 使用Vec::with_capacity为矢量预先分配内存.

Since you are iterating over two different data structures concurrently, you'll want to use the zip iterator adapter. zip stops as soon as one iterator is exhausted, so it's still relevant to validate that both vectors have the same length. zip produces an iterator of tuples, where each tuple contains the items at the same position in the two original iterators. Then you can use map to transform each tuple into the product of the two values. Finally, you'll want to collect the new iterator produced by map into a Vec which you can then return from your function. collect uses size_hint to preallocate memory for the vector using 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),Vec(& Vec)或Box(&框)作为函数参数?以获取更多信息.


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? for more information.

这篇关于如何提高Rust中按元素乘法的性能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆