1 个不稳定版本
使用旧的 Rust 2015
0.1.0 | 2017年11月5日 |
---|
#1046 在 并发 中
22KB
256 行
polyester
一些 Rust 的并行迭代器适配器
文档(即将推出!)| (手动更新 master 的文档)
这个仍在开发中的库包含一个扩展特质,Polyester
,它扩展了 Iterator
并提供了一些并行操作,允许您在多个线程间分散迭代器的消费。
要在您的项目中使用此软件包,请在 Cargo.toml 中添加以下内容
[dependencies]
polyester = "0.1.0"
...并在 crate 根目录中添加以下内容
extern crate polyester;
use polyester::Polyester;
但是,这不就是 rayon
做的吗?
并不完全一样。 rayon
是基于创建适配器来适配其自身的 ParallelIterator
特质构建的,它只能从固定长度的集合中创建。 polyester
希望从任意的 Iterator
开始,并提供任意的 Iterator
回来(或消费它们并生成一些适当的输出)。基本上,它是一个纯粹的扩展特质,用于任何已经实现了 Iterator
的东西。这意味着 polyester
有自己独特的设计目标,这使其性能特征与 rayon
不同。
架构概述
由于我正在考虑各种设计决策的影响,这个库的内部设计变化非常快,但您仍然可以查看并提出改进性能或建议额外适配器的建议。目前,这是截至写作日期(2017-10-30)的基本想法。
想要在多个线程间分散迭代器的值的主要问题是迭代器本身变成了瓶颈。只有一个迭代状态来源,需要获取下一个项目时需要对一个 &mut self
借用。目前 polyester
处理这个问题的方法是将迭代器放入后台线程,以便它可以加载到每个线程的队列中,工作线程同时获取。
这种方法有一个主要的缺点:如果预期工作线程不会执行很多每项工作,那么这总是会比按顺序执行慢。因此,当前版本的 polyester
仅在您需要为每个项目执行密集型(或间歇性密集型)工作,或者无法按顺序工作或提前收集迭代器(以将其传递给 rayon
)时推荐使用。请注意,如果您在将迭代器传递给 polyester
之前有一个昂贵的 顺序 操作,这会影响缓存填充器生成项的速度。
无论如何,一旦准备好这个“漏斗”,就会将其句柄分配给多个工作线程,以便它们可以在项目上运行用户提供的闭包。每个线程都有自己的队列来加载项目,如果其自己的队列为空,它将开始向前遍历其他线程的队列,在等待之前尝试加载更多项目。每个队列都有一个关联的 SignalEvent
(来自 synchronoise
),缓存加载工作线程将定期在填充项目或迭代器耗尽后发出信号。
从这里,每个适配器都有自己的处理过程
par_fold
par_fold
使用其项目执行基本的折叠操作:从一个 seed
累加器开始,并迭代地将项目添加到其中,直到完成。然而,由于同时发生多个折叠,需要额外的步骤来将所有“中间”累加器汇集在一起。这是通过为每个工作线程提供一个通道来完成的,将完成的累加器返回给调用线程,该线程执行“外部折叠”以合并所有累加器。
par_map
par_map
有一个更简单但实现更复杂的目标:因为它想要将并行迭代器转换回顺序迭代器,所以在将每个项目返回到另一个通道之前,它会在每个线程上执行给定的映射,该通道的 Receiver
用于相应的 ParMap
Iterator
实现中。
请注意,由于漏斗创建和处理的方式,从适配器中输出的项目 不保证顺序。(实际上,对于 par_fold
,每个线程基本上都获得每N个项目,而 outer_fold
将需要重建顺序,如果它需要的话。)
依赖项
~305KB