multiprocessing.Pool:map_async和imap有什么区别?

时间:2014-10-23 03:23:30

标签: python multiprocessing python-multiprocessing

我正在尝试学习如何使用Python的multiprocessing包,但我不理解map_asyncimap之间的区别。 我注意到map_asyncimap都是异步执行的。那我什么时候应该使用另一个呢?我该如何检索map_async返回的结果?

我应该使用这样的东西吗?

def test():
    result = pool.map_async()
    pool.close()
    pool.join()
    return result.get()

result=test()
for i in result:
    print i

2 个答案:

答案 0 :(得分:382)

imap / imap_unorderedmap / map_async之间存在两个主要差异:

  1. 他们使用你传递给他们的迭代的方式。
  2. 他们将结果返回给你的方式。
  3. map通过将iterable转换为列表来假设你的iterable(假设它已经不是列表),将其分成块,并将这些块发送到Pool中的工作进程。将iterable分解为块比执行一次一个项目的可迭代中的每个项目更好 - 特别是如果可迭代很大。但是,将iterable转换为列表以进行块化可能会产生非常高的内存成本,因为整个列表需要保存在内存中。

    imap不会将您提供的可迭代变为列表,也不会将其分成块(默认情况下)。它将一次遍历可迭代的一个元素,并将它们分别发送到工作进程。这意味着您不会将整个迭代转换为列表的内存命中,但这也意味着大型迭代的性能较慢,因为缺少分块。但是,可以通过传递大于默认值1的chunksize参数来减轻这种情况。

    imap / imap_unorderedmap / map_async之间的另一个主要区别是imap / imap_unordered,您可以开始工人准备好后立即收到工人的结果,而不是等待所有人完成工作。使用map_async,会立即返回AsyncResult,但您无法实际检索该对象的结果,直到所有对象都被处理完毕,此时它返回{{{ 1}}(map实际上在内部实现为map)。没有办法得到部分结果;你要么拥有整个结果,要么就没有结果。

    map_async(...).get()imap都会立即返回迭代。使用imap_unordered,结果将在它们准备好后立即从迭代中产生,同时仍然保留输入可迭代的顺序。使用imap,无论输入可迭代的顺序如何,只要它们准备好就会产生结果。所以,说你有这个:

    imap_unordered

    这将输出:

    import multiprocessing
    import time
    
    def func(x):
        time.sleep(x)
        return x + 2
    
    if __name__ == "__main__":    
        p = multiprocessing.Pool()
        start = time.time()
        for x in p.imap(func, [1,5,3]):
            print("{} (Time elapsed: {}s)".format(x, int(time.time() - start)))
    

    如果您使用3 (Time elapsed: 1s) 7 (Time elapsed: 5s) 5 (Time elapsed: 5s) 代替p.imap_unordered,则会看到:

    p.imap

    如果您使用3 (Time elapsed: 1s) 5 (Time elapsed: 3s) 7 (Time elapsed: 5s) p.map,则会看到:

    p.map_async().get()

    因此,使用3 (Time elapsed: 5s) 7 (Time elapsed: 5s) 5 (Time elapsed: 5s) / imap而不是imap_unordered的主要原因是:

    1. 您的iterable足够大,将其转换为列表会导致您耗尽/使用太多内存。
    2. 您希望能够在所有完成之前开始处理结果。

答案 1 :(得分:4)

已接受的答案指出,对于 imap_unordered,“结果将在准备好后立即产生”,其中人们可能会推断结果将按完成顺序返回。但我只想明确表示,一般来说并非如此。文档说明结果以任意顺序返回。考虑以下程序,该程序使用 4 的池大小、20 的 iterable 大小和 5 的 chunksize 值。工作函数根据其休眠时间可变长传递参数,这也确保池中没有一个进程获取所有提交的任务。因此,我希望池中的每个进程都有 20 / 4 = 5 个任务要处理:

from multiprocessing import Pool
import time

def worker(x):
    print(f'x = {x}', flush=True)
    time.sleep(.1 * (20 - x))
    # return approximate completion time with passed argument:
    return time.time(), x

if __name__ == '__main__':
    pool = Pool(4)
    results = pool.imap_unordered(worker, range(20), chunksize=5)
    for t, x in results:
        print('result:', t, x)

打印:

x = 0
x = 5
x = 10
x = 15
x = 16
x = 17
x = 11
x = 18
x = 19
x = 6
result: 1621512513.7737606 15
result: 1621512514.1747007 16
result: 1621512514.4758775 17
result: 1621512514.675989 18
result: 1621512514.7766125 19
x = 12
x = 1
x = 13
x = 7
x = 14
x = 2
result: 1621512514.2716103 10
result: 1621512515.1721854 11
result: 1621512515.9727488 12
result: 1621512516.6744206 13
result: 1621512517.276999 14
x = 8
x = 9
x = 3
result: 1621512514.7695887 5
result: 1621512516.170747 6
result: 1621512517.4713914 7
result: 1621512518.6734042 8
result: 1621512519.7743165 9
x = 4
result: 1621512515.268784 0
result: 1621512517.1698637 1
result: 1621512518.9698756 2
result: 1621512520.671273 3
result: 1621512522.2716706 4

您可以清楚地看到这些结果不是按完成顺序产生的。例如,我返回了 1621512519.7743165 9 后跟 1621512515.268784 0,这是工作函数比之前返回的结果早 4 秒以上返回的。但是,如果我将 chunksize 值更改为 1,则打印输出变为:

x = 0
x = 1
x = 2
x = 3
x = 4
result: 1621513028.888357 3
x = 5
result: 1621513028.9863524 2
x = 6
result: 1621513029.0838938 1
x = 7
result: 1621513029.1825204 0
x = 8
result: 1621513030.4842813 7
x = 9
result: 1621513030.4852195 6
x = 10
result: 1621513030.4872172 5
x = 11
result: 1621513030.4892178 4
x = 12
result: 1621513031.3908074 11
x = 13
result: 1621513031.4895358 10
x = 14
result: 1621513031.587289 9
x = 15
result: 1621513031.686152 8
x = 16
result: 1621513032.1877549 15
x = 17
result: 1621513032.1896958 14
x = 18
result: 1621513032.1923752 13
x = 19
result: 1621513032.1923752 12
result: 1621513032.2935638 19
result: 1621513032.3927407 18
result: 1621513032.4912949 17
result: 1621513032.5884912 16

这个的完成顺序。但是,我犹豫要声明 imap_unordered 将始终在结果可用时返回结果如果指定了 chunksize 值为 1,尽管基于这个实验似乎是这种情况,因为文档没有做出这样的声明。

讨论

当指定 chunksize 为 5 时,20 个任务被放置在单个输入队列中,供池中的 4 个进程以大小为 5 的块进行处理。因此,空闲的进程将将下一个由 5 个任务组成的块从队列中取出,并在再次空闲之前依次处理它们中的每一个。因此,第一个进程将处理 x 参数 0 到 4,第二个进程将处理 x 参数 5 到 9,等等。这就是您看到初始 x 值打印为 0, 5 的原因、10 和 15。

但是,虽然 x 参数 0 的结果在 x 参数 9 的结果之前完成,但结果似乎是作为块一起写出的,因此 x 参数的结果在同一块(即 1、2、3 和 4)中排队的 x 参数的结果也可用之前,不会返回 0。