2

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

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

我读到了ParallelIterator::weight(0.6.0),但我不明白我是否应该为小型收藏品优化这种极端情况,或者 Rayon 是否聪明并处理引擎盖下的所有事情。

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

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

谷歌把我送到了一个旧的文档页面。自 0.8.0 版以来Weight已弃用并删除。

4

2 回答 2

2

不推荐使用权重 API,而支持拆分长度控制。默认情况下,Rayon 将在每个 item处拆分,有效地使所有计算并行化,此行为可以通过 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)]

游乐场(感谢@shepmaster 提供代码)

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

于 2019-02-09T17:15:59.023 回答