Byt*_*der 7 performance multiprocessing python-3.x python-multiprocessing
如何找到multiprocessing.Pool实例的最佳块大小?
之前我用过这个来创建一个nsudoku对象的生成器:
processes = multiprocessing.cpu_count()
worker_pool = multiprocessing.Pool(processes)
sudokus = worker_pool.imap_unordered(create_sudoku, range(n), n // processes + 1)
Run Code Online (Sandbox Code Playgroud)
为了测量时间,我time.time()在上面的片段之前使用,然后按照描述初始化池,然后我将生成器转换为list(list(sudokus))以触发生成项目(仅用于时间测量,我知道这在最终程序中是无意义的),然后我time.time()再次使用时间并输出差异.
我观察到每个对象的块大小n // processes + 1约为0.425毫秒.但我也观察到CPU只在整个过程的前半部分完全加载,最终使用率下降到25%(在具有2核和超线程的i3上).
如果我使用较小的块大小int(l // (processes**2) + 1),我会得到大约0.355毫秒的时间,并且CPU负载分布更好.它只有一些小的尖峰到ca. 75%,但在处理时间的长时间内保持高位,然后降至25%.
是否有更好的公式来计算块大小或更好的方法来使用CPU最有效?请帮助我提高这个多处理池的有效性.
max*_*max 12
这个答案提供了高水平的概述.
进入细分,每个工人一次被送去一大堆chunksize任务进行处理.每当工人完成该块时,它需要通过某种类型的进程间通信(IPC)请求更多输入,例如queue.Queue.每个IPC请求都需要系统调用; 由于上下文切换,它的成本在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而创建的开销也是如此.