我有MyReader
实施Iterator
并生成Buffer
s Buffer : Send
。 MyReader
很快产生了很多Buffer
,但是我有一个CPU密集型工作要在每个Buffer
(.map(|buf| ...)
)上执行,这是我的瓶颈,然后收集结果(订购)。我希望将CPU密集型工作并行化 - 希望用于N个线程,这将使用工作窃取来执行它们的速度与核心数量允许的速度一样快。
编辑:更准确。我正在研究rdedup
。 MyStruct
是Chunker
,它读取io::Read
(通常是stdio),查找数据的部分(块)并生成它们。然后,对于每个块,假设map()
计算sha256摘要,压缩,加密,保存并返回摘要作为map(...)
的结果。已保存数据的摘要用于构建数据的index
。 map(...)
处理的块之间的顺序无关紧要,但每个map(...)
返回的摘要需要按照找到块的顺序收集。实际的save
到文件步骤被卸载到另一个线程(写入线程)。 actual code of PR in question
我希望我可以使用rayon
,但是rayon
期望一个已经可以并行化的迭代器 - 例如。一个Vec<...>
或类似的东西。我发现无法从par_iter
获得MyReader
- 我的读者本质上是单线程的。
有simple_parallel
,但文档说它不建议用于一般用途。我想确保一切都能正常运作。
我可以采用spmc队列实现和自定义thread_pool
,但我正在寻找经过优化和测试的现有解决方案。
还有pipeliner
,但还没有支持有序地图。
答案 0 :(得分:5)
一般而言,就并行化而言,保留顺序是一项非常艰难的要求。
您可以尝试使用典型的扇出/扇入设置进行手工制作:
或者你可以提高抽象水平。
特别感兴趣:Future
。
Future
表示计算的结果,可能已经或可能没有。接收Future
的有序列表的消费者可以简单地等待每个消息,并让缓冲在队列中自然发生。
对于奖励积分,如果您使用固定大小的队列,您将自动对消费者施加压力。
因此我建议建立一些CpuPool
。
设置将是:
use std::sync::mpsc::{Receiver, Sender};
fn produce(sender: Sender<...>) {
let pool = CpuPool::new_num_cpus();
for chunk in reader {
let future = pool.spawn_fn(|| /* do work */);
sender.send(future);
}
// Dropping the sender signals there's no more work to consumer
}
fn consume(receiver: Receiver<...>) {
while let Ok(future) = receiver.recv() {
let item = future.wait().expect("Computation Error?");
/* do something with item */
}
}
fn main() {
let (sender, receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || consume(receiver));
produce(sender);
}