我正在试验新的 Shiny concurrent.futures Python 3.2 中引入的模块,我注意到,几乎使用相同的代码,使用 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 article 的简单分解函数,这些是我电脑上的结果(i7, 64-bit, 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 错误。有什么想法吗?
最佳答案
当使用 concurrent.futures
中的 map
时,每个元素都来自可迭代的 is submitted separately到执行器,它会创建一个 Future
每次调用的对象。然后它返回一个迭代器,该迭代器产生 future 返回的结果。
Future
对象是相当重量级的,它们做了很多工作来允许它们提供的所有功能(如回调、取消能力、检查状态......)。
与此相比,multiprocessing.Pool
的开销要少得多。批量提交作业(减少IPC开销),直接使用函数返回的结果。对于大批量的工作,多处理绝对是更好的选择。
如果您想汇总长期运行的作业,而开销并不那么重要,您希望通过回调收到通知或不时检查它们是否已完成或能够取消单独执行。
个人笔记:
我真的想不出太多使用 Executor.map
的理由——它没有给你任何 future 的特性——除了指定超时的能力。如果您只对结果感兴趣,最好使用 multiprocessing.Pool
的映射函数之一。
关于python - 来自 concurrent.futures 的 ProcessPoolExecutor 比 multiprocessing.Pool 慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18671528/