Rayon 会避免为少量工作生成线程吗?

Will Rayon avoid spawning threads for a small amount of work?

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

并行性开销有时会导致小型集合变慢。如果我为多线程做必要的准备,迭代 2 个元素会比使用单线程版本慢。如果我有 4000 万个元素,并行性会给我线性的性能提升。

我阅读了有关 ParallelIterator::weight (0.6.0) 的内容,但我不明白我是否应该针对小型集合优化此类极端情况,或者 Rayon 是否很聪明并且可以处理所有事情。

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

处理后的元素ParallelIterator::weight为1,定义好见相关文档,但处理单个元素成本低

Google 将我转到旧文档页面。 Weight 已弃用,removed 自版本 0.8.0.

您可以凭经验看出这种行为是无法保证的:

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 创建一个全局线程池并使用工作窃取来平衡线程之间的工作。线程池是每个进程的一次性设置成本,工作窃取有助于确保工作仅在需要时跨越线程边界。这就是为什么上面的输出都使用相同的线程。

权重 API 已弃用,取而代之的是 split length control。 默认情况下,Rayon 将在 每个项目 处拆分,有效地使所有计算并行,此行为可以通过配置 with_min_len.

Sets the minimum length of iterators desired to process in each thread. Rayon will not split any smaller than this length, but of course an iterator could already be smaller to begin with.

Producers like zip and interleave will use greater of the two minimums. Chained iterators and iterators inside flat_map may each use their own minimum length.

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 提供代码)