人造丝会避免为少量工作而生成线程吗?

时间:2019-02-09 11:17:28

标签: multithreading rust rayon

我当时正在考虑使用Rayon的并行迭代器功能,但是我担心迭代小型集合的性能。

并行化开销有时可能会导致小型集合的运行速度变慢。如果我为多线程做必要的准备工作,则与使用单线程版本相比,迭代2个元素要慢。如果我有4000万个元素,那么并行将使我的线性性能得到改善。

我了解了ParallelIterator::weight (0.6.0),但是我不知道我是否应该为小型藏品优化这种极端情况,或者人造丝很聪明并且可以在幕后处理所有事情。

if collection_is_small() {
    // Run single threaded version... 
} else {
    // Use parallel iterator.
}

已处理元素的ParallelIterator::weight为1。有关正确定义,请参阅相关文档,但是处理单个元素很便宜。

Google将我转到了旧的文档页面。从版本0.8.0开始,Weight已过时,removed已弃用。

2 个答案:

答案 0 :(得分:1)

您可以凭经验看到这种行为无法得到保证:

use rayon::prelude::*; // 1.0.3

use std::thread;

fn main() {
    let ids: Vec<_> = (0..2)
        .into_par_iter()
        .map(|_| thread::current().id())
        .collect();
    println!("{:?}", ids);
}

程序的各种运行显示:

[ThreadId(1), ThreadId(2)]
[ThreadId(1), ThreadId(1)]
[ThreadId(2), ThreadId(1)]
[ThreadId(2), ThreadId(2)]

话虽如此,您应该执行自己的基准测试。默认情况下,Rayon创建一个全局线程池,并使用工作窃取来平衡线程之间的工作。线程池是每个进程的一次性设置成本,工作窃取有助于确保工作仅在需要时才跨越线程边界。这就是为什么上面有两个使用相同线程的输出的原因。

答案 1 :(得分:0)

不推荐使用重量API,而推荐使用split length control。 默认情况下,Rayon将在每个项目处拆分,从而有效地使所有计算并行进行,可以通过以下方式配置此行为 with_min_len

  

设置希望在每个线程中处理的迭代器的最小长度。人造丝不会分裂得比这个长度小,但是当然迭代器开始时可能会更小。

     

诸如zip和interleave之类的生产者将使用两个最小值中的较大者。 flat_map中的链式迭代器和迭代器可能各自使用自己的最小长度。

extern crate rayon; // 1.0.3
use rayon::prelude::*;
use std::thread;

fn main() {
    println!("Main thread: {:?}", thread::current().id());
    let ids: Vec<_> = (0..4)
        .into_par_iter()
        .with_min_len(4)
        .map(|_| thread::current().id())
        .collect();
    println!("Iterations: {:?}", ids);
}

输出:

Main thread: ThreadId(0)
Iterations: [ThreadId(0), ThreadId(0), ThreadId(0), ThreadId(0)]

Playground(感谢@shepmaster提供代码)