49

我正在试验 Python 3.2 中引入的新的闪亮concurrent.futures模块,我注意到,几乎使用相同的代码,使用来自 concurrent.futures 的 Pool使用multiprocessing.Pool慢得多。

这是使用多处理的版本:

def hard_work(n):
    # Real hard work here
    pass

if __name__ == '__main__':
    from multiprocessing import Pool, cpu_count

    try:
        workers = cpu_count()
    except NotImplementedError:
        workers = 1
    pool = Pool(processes=workers)
    result = pool.map(hard_work, range(100, 1000000))

这是使用concurrent.futures:

def hard_work(n):
    # Real hard work here
    pass

if __name__ == '__main__':
    from concurrent.futures import ProcessPoolExecutor, wait
    from multiprocessing import cpu_count
    try:
        workers = cpu_count()
    except NotImplementedError:
        workers = 1
    pool = ProcessPoolExecutor(max_workers=workers)
    result = pool.map(hard_work, range(100, 1000000))

使用从Eli Bendersky 这篇文章中提取的简单分解函数,这些是在我的计算机(i7、64 位、Arch Linux)上的结果:

[juanlu@nebulae]─[~/Development/Python/test]
└[10:31:10] $ time python pool_multiprocessing.py 

real    0m10.330s
user    1m13.430s
sys 0m0.260s
[juanlu@nebulae]─[~/Development/Python/test]
└[10:31:29] $ time python pool_futures.py 

real    4m3.939s
user    6m33.297s
sys 0m54.853s

我无法使用 Python 分析器分析这些,因为我遇到了 pickle 错误。有任何想法吗?

4

1 回答 1

69

使用mapfromconcurrent.futures时,可迭代的每个元素都单独提交给执行程序,执行程序为每个调用创建一个Future对象。然后它返回一个迭代器,该迭代器产生期货返回的结果。
Future对象是相当重量级的,它们做了很多工作来允许它们提供的所有功能(如回调、取消能力、检查状态......)。

与此相比,multiprocessing.Pool开销要少得多。批量提交作业(减少IPC开销),直接使用函数返回的结果。对于大批量的工作,多处理绝对是更好的选择。

如果您想汇总开销不那么重要的长时间运行的作业,希望通过回调通知或不时检查以查看它们是否已完成或能够单独取消执行,则期货非常有用。

个人备注

我真的想不出太多使用的理由Executor.map——它没有给你任何期货的特性——除了指定超时的能力。如果您只对结果感兴趣,最好使用multiprocessing.Pool的 map 函数之一。

于 2013-09-07T10:09:37.170 回答