来自concurrent.futures的ProcessPoolExecutor比multiprocessing.Pool慢

时间:2013-09-07 08:45:18

标签: python concurrency multiprocessing future concurrent.futures

我正在尝试使用Python 3.2中引入的新的闪亮concurrent.futures模块,我注意到,几乎使用相同的代码,使用来自concurrent.futures的池是方式更慢而不是使用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 article的天真因子分解函数,这些是我的计算机上的结果(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 个答案:

答案 0 :(得分:46)

当使用map中的concurrent.futures时,可迭代is submitted中的每个元素分别与执行者一起创建Future个对象。然后它返回一个迭代器,它产生期货返回的结果 Future个对象相当重量级,他们做了很多工作来允许他们提供的所有功能(如回调,取消,检查状态......)。

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

如果您希望在开销不重要的长期工作中进行总结,那么期货很有用,您希望通过回调或不时检查来查看它们是否已完成或能够取消单独执行。

个人笔记

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