【问题标题】:Create a set with multiprocessing使用多处理创建集合
【发布时间】:2018-02-17 19:36:00
【问题描述】:

我有一个很大的项目清单和一些辅助数据。对于列表中的每个项目和数据中的元素,我计算一些东西,并将所有东西添加到输出集中(可能有很多重复项)。在代码中:

def process_list(myList, data):
    ret = set()
    for item in myList:
        for foo in data:
            thing = compute(item, foo)
            ret.add(thing)
    return ret

if __name__ == "__main__":
    data = create_data()
    myList = create_list()
    what_I_Want = process_list(myList, data)

因为 myList 很大并且 compute(item, foo) 成本很高,所以我需要使用多处理。现在这就是我所拥有的:

from multiprocessing import Pool

initialize_worker(bar):
    global data
    data = bar

def process_item(item):
    ret = set()
    for foo in data:
        thing = compute(item, foo)
        ret.add(thing)
    return ret

if __name__ == "__main__":
    data = create_data()
    myList = create_list()
    p = Pool(nb_proc, initializer = initialize_worker, initiargs = (data))
    ret = p.map(process_item, myList)
    what_I_Want = set().union(*ret)

我不喜欢的是 ret 可能很大。我正在考虑 3 个选项:

1) 将 myList 切成块并将它们传递给工作人员,他们将在每个块上使用 process_list(因此在该步骤将删除一些重复项),然后合并所有获得的集合以删除最后的重复项。

问题:有没有一种优雅的方式来做到这一点?我们可以向 Pool.map 指定它应该将块传递给工作人员而不是块中的每个项目吗?我知道我可以自己删除列表,但这太丑了。

2) 在所有进程之间有一个共享集。

问题:为什么 multiprocessing.manager 没有功能 set()? (我知道它有 dict(),但仍然......)如果我使用 manager.dict(),进程和管理器之间的通信不会大大减慢速度吗?

3) 有一个共享的 multiprocessing.Queue()。每个工人将它计算的东西放入队列中。另一个工人进行联合,直到找到一些 stopItem(我们将其放入 p.map 之后的队列中)

问题:这是一个愚蠢的想法吗?进程和 multiprocessing.Queue 之间的通信是否比使用 manager.dict() 更快?另外,我怎样才能取回由进行联合的工人计算的集合?

【问题讨论】:

  • 我不会将此作为答案添加,因为我认为Javier 的答案已经涵盖了所需内容。我个人会使用mp.Queue,主要是因为我认为它更清晰,并且可能对整个过程有更多的控制。就最快而言,这在很大程度上取决于实际的计算负载,如果每次调用 computeThing 需要一个小时,那么托管数据结构的开销可能可以忽略不计。因此,为了让您知道什么是最好的,您需要在特定负载上对其进行测试。

标签: python python-multiprocessing


【解决方案1】:

一件小事:initiargs 接受一个元组。

如果您想避免在将结果缩减为set 之前创建所有结果,您可以使用Pool.imap_unordered() 和一些块大小。这将在每个工作人员可用时产生块大小结果。

如果您想将process_item 更改为直接接受块,则必须手动进行。 toolz.partition_all 可用于对初始数据集进行分区。

最后,托管数据结构必然具有更高的同步开销。我会尽量避开它们。

使用imap_unordered 看看是否足够好;如果不是,则分区;如果您无法避免总共有两个以上的重复项,请使用托管字典。

【讨论】:

  • 或者Pool.imap_unordered(),这通常比imap()快一点,因为它不需要关心退货单,而且它们最终还是在一个无序的容器中。
  • 好主意,我会修改我的答案!
  • 谢谢!太好了,我不知道 imap(和 imap_unordered)一旦准备好就会产生结果。然而,使用这种方法,Pool.imap_unordered() 似乎仍然很大,因为在某些时候整个迭代是在内存中计算的。有没有办法让它成为一个生成器,一生成就忘记结果?
  • 它是一个生成器。
猜你喜欢
  • 2017-02-28
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-20
  • 2015-04-19
  • 1970-01-01
相关资源
最近更新 更多