2

我正在尝试并行化我的一部分代码,尽管它使用rayon和并行迭代器par_iter()and par_extend(),但它看起来仍然像在单个线程上运行。

我只是创建一个 的向量i32,用很多值填充它,然后将这些值移动到collections::HashSet整数中。

我的单线程代码:

use std::collections::HashSet;

fn main() {
    let my_vec: Vec<i64> = (0..100_000_000).collect();

    let mut my_set: HashSet<i64> = HashSet::new();
    let st = std::time::Instant::now();
    my_set.extend(
        my_vec.iter().map(|x| x*(x+3)/77+44741)  // this is supposed to take a while to compute
    );
    let dur = st.elapsed();
    println!("{:?}", dur);

}

运行时间大约8.86 s是平均水平。这是使用并行迭代器的代码:

extern crate rayon;
use rayon::prelude::*;
use std::collections::HashSet;

fn main() {
    let my_vec: Vec<i64> = (0..100_000_000).collect();

    let mut my_set: HashSet<i64> = HashSet::new();
    let st = std::time::Instant::now();
    my_set.par_extend(
        my_vec.par_iter().map(|x| x*(x+3)/77+44741) // this is supposed to take a while to compute
    );
    let dur = st.elapsed();
    println!("{:?}", dur);
}

8.62 s“并行”版本的 平均运行时间几乎相同(

你知道我做错了什么,或者不明白吗?

4

1 回答 1

3

您的模拟是不正确的,因为您的计算实际上很快,如此之快以至于它比线程上下文切换快几个数量级。您 100% 的核心可能是人造丝运行时,而其他核心正在等待它。

如果您实际上将计算替换为睡眠,则结果与您预期的一样:

use std::collections::HashSet;
use rayon::prelude::*; // 1.1.0
use std::time::Duration;

fn main() {
    fn slow(i: &i64) -> i64 {
        std::thread::sleep(Duration::from_millis(5));

        *i
    }

    let my_vec: Vec<i64> = (0..100).collect();

    let mut my_set: HashSet<i64> = HashSet::new();

    let st = std::time::Instant::now();
    my_set.extend(
        my_vec.iter().map(slow)  // this is supposed to take a while to compute
    );
    let dur = st.elapsed();
    println!("Regular: {:?}", dur);

    let st = std::time::Instant::now();
    my_set.par_extend(
        my_vec.par_iter().map(slow) // this is supposed to take a while to compute
    );
    let dur = st.elapsed();
    println!("Rayon: {:?}", dur);
}

输出:

Regular: 685.670791ms
Rayon: 316.733253ms

当您尝试优化您的代码时,您必须仔细对其进行基准测试,因为有时,当您并行化您的代码时,这可能会使其变慢。

于 2019-07-12T14:01:40.613 回答