concurrent.futures中的ProcessPoolExecutor比multiprocessing.Pool慢得多

53
我正在尝试使用Python 3.2中引入的新模块concurrent.futures进行实验,并且注意到,几乎相同的代码,使用multiprocessing.Pool比使用concurrent.futures中的Pool慢得多。以下是使用multiprocessing的版本:
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错误。有什么想法吗?

1
你能否发布一个关于此事的更新吗?也许是版本3.8? - Oren
1个回答

73
当使用来自concurrent.futuresmap时,可迭代对象中的每个元素都会单独提交给执行器,该执行器为每个调用创建一个Future对象。然后它返回一个迭代器,该迭代器产生由这些future返回的结果。
Future对象相当笨重,它们做了很多工作以提供它们所提供的所有功能(如回调、取消能力、检查状态等)。
相比之下,multiprocessing.Pool的开销要小得多。它将作业分批提交(减少IPC开销),并直接使用函数返回的结果。对于大批量的作业,多进程绝对是更好的选择。
"Futures非常适合提交运行时间较长的任务,其中开销不是那么重要的情况下。您可以通过回调或定期检查来接收通知,以查看它们是否完成或能够单独取消执行。"
"个人笔记:我真的想不出使用Executor.map的原因 - 它没有提供任何Future的功能 - 除了能够指定超时时间。如果您只对结果感兴趣,最好使用multiprocessing.Pool的一个map函数。"

非常感谢您的回答!也许批量提交是关键。 - astrojuanlu
14
值得一提的是,在 Python 3.5 中,ProcessPoolExecutor.map 函数将接受一个名为 chunksize 的关键字参数,这将在一定程度上缓解 IPC 开销问题。有关更多信息,请参阅此错误 - dano
此外,在Python 3.2中,您可以为多进程池设置_maxtasksperchild_,在我的情况下,这有助于在每个工作进程完成其工作负载后清理资源。链接 - Kieleth
3
жҲ‘жӣҙе–ңж¬ўдҪҝз”ЁProcessPoolExecutor.map()пјҢеӣ дёәеңЁmp.Pool.map()дёӯеӯҳеңЁиҝҷдёӘbugгҖӮ - Ciprian Tomoiagă
1
看起来@Ciprian提到的那个漏洞仍然存在,并且有一些未完成的尝试来修复它,最新的尝试是https://github.com/python/cpython/pull/16103。 - astrojuanlu
1
有人可以更新一下Python 3.7+的性能吗? - Volatil3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接