futures.ProcessPoolExecutor的速度慢:如何改进?

时间:2020-10-17 21:42:46

标签: python multiprocessing

我试图了解以下代码为何如此缓慢:

import threading
import time
import concurrent.futures
from datetime import datetime


def dump(txt):
    print(f'[{datetime.now()}] ({threading.get_ident():05}) {txt}\n', end='')


def sleep_(_):
    dump('Start')
    time.sleep(0.1)
    dump('Stop')


def main(n=10, processes=10):
    dump('before with')
    with concurrent.futures.ProcessPoolExecutor(processes) as pool:
        dump('before map')
        tmp = list(pool.map(sleep_, range(n)))
        dump('after map')
    dump('after with')


if __name__ == '__main__':
    main()

这是结果:

[2020-10-17 23:34:12.822813] (07100) before with
[2020-10-17 23:34:12.824808] (07100) before map
[2020-10-17 23:34:21.409045] (14100) Start
[2020-10-17 23:34:21.414031] (15408) Start
[2020-10-17 23:34:21.414031] (20292) Start
[2020-10-17 23:34:21.415029] (18972) Start
[2020-10-17 23:34:21.416026] (13660) Start
[2020-10-17 23:34:21.416026] (10904) Start
[2020-10-17 23:34:21.417023] (18828) Start
[2020-10-17 23:34:21.418021] (18616) Start
[2020-10-17 23:34:21.504788] (01776) Start
[2020-10-17 23:34:21.509775] (14100) Stop
[2020-10-17 23:34:21.509775] (14100) Start
[2020-10-17 23:34:21.514761] (20292) Stop
[2020-10-17 23:34:21.514761] (15408) Stop
[2020-10-17 23:34:21.515760] (18972) Stop
[2020-10-17 23:34:21.516757] (13660) Stop
[2020-10-17 23:34:21.516757] (10904) Stop
[2020-10-17 23:34:21.517754] (18828) Stop
[2020-10-17 23:34:21.518751] (18616) Stop
[2020-10-17 23:34:21.605519] (01776) Stop
[2020-10-17 23:34:21.610506] (14100) Stop
[2020-10-17 23:34:21.611503] (07100) after map
[2020-10-17 23:34:23.281562] (07100) after with

我想在这里理解的是为什么要花近9秒钟才能启动第一个过程。为什么要花近2秒钟来清理它们?

这是Windows系统(正在调试)。 当我正常运行它时,向上旋转和向下滚动将花费+-0.5s。

与在同一系统上的Debian WSL相比:旋转0.04秒,结束时0.006s。

这是正常行为吗?和/或如何对其进行改进?为什么会这样?

谢谢!

2 个答案:

答案 0 :(得分:0)

Windows没有等效于fork系统调用来复制进程。这意味着在Windows中使用具有10个工作进程的多处理池将创建10个 python进程。

启动第一个进程不需要9秒,启动池中的所有10个进程然后将目标函数激发到第一个进程需要9秒。

答案 1 :(得分:0)

在Linux和其他类似Unix的操作系统上,进程是通过fork系统调用创建的,该系统调用基本上会创建主进程的惰性副本(写时复制)。这是一个快速的操作。

Windows没有fork功能,因此必须通过启动新的解释器并执行完整的Python代码(末尾受保护的if __name__ == '__main__':部分除外)来创建每个工作进程,以在建立进程之前可以使用。 Python在multiprocessing docs中将此方法称为“生成”方法。

自Python 3.8起,spawn方法也是MacOS上的默认设置。

相关问题