例如,将6个任务中的17个任务分配给2个进程将是2 * 6 + 5,也就是说,每个进程得到6个,但剩下5个任务。我认为最好的方法是让剩余的任务在2个进程中均匀分布。所以我做了一个测试,看看mp.Pool
是否正在这样做。这是一个测试脚本
import multiprocessing as mp
import time
def f(x):
time.sleep(0.1)
print(mp.current_process())
if __name__ == "__main__":
p = mp.Pool(2)
p.map(f, range(17),6)
将输出
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-2, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-2, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-2, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-2, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-2, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-2, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
<ForkProcess(ForkPoolWorker-1, started daemon)>
注意最后5行,显然剩下的任务全部分配到一个进程,然后所有其他进程都无关。
是否有一种在Pool.map
中自动分发剩余块的好方法?
答案 0 :(得分:2)
不是简单的参数变化。并且当您将流程分配给工作人员时,您不会重新分配流程。
当然你可以写一个解决方法:
if __name__ == "__main__":
p = mp.Pool(2)
tasks = 17
chunksize = 6
p.map(f, range((tasks / chunksize) * chunksize), 6)
p.map(f, range((tasks / chunksize) * chunksize + 1, tasks))
这首先是完整的块。然后将剩余的五个任务逐个分配给池而不是块。这将产生预期的结果。
但是,我不确定为什么这很重要。如果任务需要很长时间才能完成,那么使用块有什么意义呢?随着时间花在工人身上,不会有任何表现提升。默认的块大小为1时,您可以完全正常工作,只要一个任务空闲,就可以将任务分配给工作人员。 如果任务在极短的时间内完成,并且将更多时间用于逐个向工作人员传输数据,那么Chunksize可以提升性能。如果是这种情况,那么如果最后一个任务是由一个工人完成的,那么完成这几个任务的时间将是无关紧要的并不重要。这个特殊的例子当然是最糟糕的情况。在更现实的情况下,通常情况下不会有任何性能损失。想象一下,成批的数万个任务以35个块的形式发送给工人。不可避免的是,在批处理过程中,工人们分道扬and,他们没有完全同时完成。一些块会更快,一些更慢。现在让我们假设您的余数不是34而是17.如果工作者A现在已经启动了最后一个完整的块并且当工作者B完成其最后一个完整的块并且得到17的剩余部分时处理了不到一半的任务,那么它将是可能工人B实际上在工人A之前完成,尽管已处理剩余集。
但是如果有理由先做块,剩下的就分发给工人,那么这就是做这件事的方法。我确实认为您正在尝试解决一个不是问题的问题,而且为了难以衡量的性能提升,您只会为代码添加更多复杂性。