Python 3多处理:最佳块大小

时间:2016-01-25 09:09:25

标签: performance python-3.x multiprocessing python-multiprocessing

如何找到multiprocessing.Pool个实例的最佳块大小?

之前我用过这个来创建n数独对象的生成器:

processes = multiprocessing.cpu_count()
worker_pool = multiprocessing.Pool(processes)
sudokus = worker_pool.imap_unordered(create_sudoku, range(n), n // processes + 1)

要测量时间,我在上面的代码段之前使用time.time(),然后按照描述初始化池,然后将生成器转换为列表(list(sudokus))以触发生成项目(仅对于时间测量,我知道这在最终程序中是无意义的),然后我再次使用time.time()并输出差异。

我观察到n // processes + 1的块大小导致每个对象大约 0.425 ms 的时间。但我也观察到CPU只在整个过程的前半部分完全加载,最终使用率降至25%(在具有2核和超线程的i3上)。

如果我使用较小的int(l // (processes**2) + 1)块大小,我会得到大约 0.355 ms 的时间,并且CPU负载分布更好。它只有一些小的尖峰到ca. 75%,但在处理时间的长时间内保持高位,然后降至25%。

是否有更好的公式来计算块大小或更好的方法来使用CPU最有效?请帮助我提高这个多处理池的有效性。

2 个答案:

答案 0 :(得分:10)

This answer提供了高级概述。

进入详细状态时,每个工作人员一次被发送一大块chunksize个任务进行处理。每次工作人员完成该块时,都需要通过某种类型的进程间通信(IPC)请求更多输入,例如queue.Queue。每个IPC请求都需要系统调用;由于上下文切换,它的成本为anywhere in the range of 1-10 μs,假设为10μs。由于共享缓存,上下文切换可能会(在有限程度上)损害所有核心。所以极其悲观地让我们估算出100μs的IPC请求的最大可能成本。

你希望IPC开销不重要,比方说< 1%。如果我的数字正确,您可以通过使块处理时间> 10 ms来确保。因此,如果每个任务需要1μs来处理,那么您至少需要chunksize 10000

不使chunksize任意大的主要原因是,在执行结束时,其中一个工作人员可能仍在运行而其他人都已完成 - 显然不必要地增加完成时间。我想在大多数情况下延迟10毫秒并不是什么大问题,所以我建议定位10毫秒的块处理时间似乎是安全的。

chunksize可能导致问题的另一个原因是准备输入可能需要时间,同时浪费工人的能力。据推测,输入准备比处理更快(否则它应该并行化,使用像RxPY这样的东西)。所以再次针对~10 ms的处理时间似乎是安全的(假设您不介意启动延迟小于10毫秒)。

注意:对于现代Linux / Windows上的非实时进程,上下文切换每隔约1-20毫秒左右发生一次 - 除非该进程事先进行系统调用。因此,没有系统调用,上下文切换的开销不超过1%。由于IPC的原因,你所创造的开销也是如此。

答案 1 :(得分:0)

没有什么能取代实际的时间测量。我不打扰公式并尝试使用常量,例如1,10,100,1000,10000,看看哪种情况最适合你的情况。