如何将生成器用作具有多处理映射函数的可迭代函数

时间:2017-06-22 19:57:49

标签: python multiprocessing

当我使用生成器作为multiprocessing.Pool.map函数的可迭代参数时:

pool.map(func, iterable=(x for x in range(10)))

在调用func之前,似乎发生器已完全耗尽。

我希望产生每个项目并将其传递给每个流程,谢谢

3 个答案:

答案 0 :(得分:3)

multiprocessing.map在处理之前将没有__len__方法的迭代转换为列表。这样做是为了帮助计算chunksize,池用于对工作程序参数进行分组并降低调度作业的往返成本。这不是最优的,特别是当chunksize为1时,但由于map必须以某种方式耗尽迭代器,因此它通常不是一个重要的问题。

相关代码位于pool.py。请注意它使用len

def _map_async(self, func, iterable, mapper, chunksize=None, callback=None,
        error_callback=None):
    '''
    Helper function to implement map, starmap and their async counterparts.
    '''
    if self._state != RUN:
        raise ValueError("Pool not running")
    if not hasattr(iterable, '__len__'):
        iterable = list(iterable)

    if chunksize is None:
        chunksize, extra = divmod(len(iterable), len(self._pool) * 4)
        if extra:
            chunksize += 1
    if len(iterable) == 0:
        chunksize = 0

答案 1 :(得分:1)

唉,这个定义不明确。这是我在Python 3.6.1下运行的测试用例:

import multiprocessing as mp

def e(i):
    if i % 1000000 == 0:
        print(i)

if __name__ == '__main__':
    p = mp.Pool()
    def g():
        for i in range(100000000):
            yield i
        print("generator done")
    r = p.map(e, g())
    p.close()
    p.join()

你看到的第一件事是"发电机完成"消息,并且峰值内存使用量过高(正如您怀疑的那样,生成器在任何工作结束之前都会耗尽)。

但是,请更换map()这样的电话:

r = list(p.imap(e, g()))

现在内存使用仍然很小,并且"生成器已完成"出现在输出端。

然而,你不会等待很长时间才能看到它,因为它非常缓慢:-(imap()不仅将可迭代的视为是一个可迭代的,但是有效地跨过程边界一次只传递1个项目。为了获得速度,这也有效:

r = list(p.imap(e, g(), chunksize=10000))

在现实生活中,我更有可能迭代imap()(或imap_unordered())结果,而不是强迫它进入列表,然后内存使用仍然很小,无法循环结果也是。

答案 2 :(得分:1)

以 Tim Peters 的回答为基础,这里有一个 jupyter notebook,展示了 imap 和 chunksize 之间的相互作用:

https://gist.github.com/shadiakiki1986/273b3529d3ff7afe2f2cac7b5ac96fe2

它有两个例子:

示例 1 使用 chunksize=1 并具有以下执行:

    On CPU 1, execute item 1 from generator
    On CPU 2, execute item 2 from generator
    When CPU 1 done with item 1, execute item 3 from generator
    When CPU 2 done with item 2, execute item 4 from generator
    etc

示例 2 的 chunksize=3 执行以下操作

    On CPU 1, execute items 1-3 from generator
    On CPU 2, execute items 4-6 from generator
    When CPU 1 done with items 1-3, execute on 7-9
    When CPU 2 done with items 4-6, execute on 10

请注意,在示例 2 中,第 10 项在 CPU 2 上执行,然后在 CPU 1 上执行第 8 项和第 9 项。