使用"spawn"启动方法了解 python 多处理子进程内存使用情况



我正在尝试使用多处理模块在python 3.8.0中创建子进程,而无需继承父进程的内存。我为此使用生成启动方法mp.set_start_method('spawn')。但子进程的内存使用量几乎与父进程相同。下面的代码片段

我正在使用此处共享的代码进行测试 如何限制多处理进程的范围?

memtest.py

import multiprocessing  as mp
import numpy as np
def foo(x):
import time
time.sleep(60)
if __name__ == "__main__":
mp.set_start_method('spawn')
dont_inherit = np.ones((500, 100))
for x in range(3):
mp.Process(target=foo, args=(x,)).start()

使用python3 memtest.py运行

从顶部开始的内存使用情况

449m  28m  14m S  0.0  0.2   0:00.44 python3 memtest.py
34904  10m 5816 S  0.0  0.1   0:00.03 /srv/env/bin/python3 -c from multiprocessing.resource_tracker import main;main(5)
252m  26m  13m S  0.0  0.2   0:00.26 /srv/env/bin/python3 -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=6, pipe_handle=20) --multiprocessing-fork
252m  27m  13m S  0.0  0.2   0:00.21 /srv/env/bin/python3 -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=6, pipe_handle=22) --multiprocessing-fork
252m  26m  13m S  0.0  0.2   0:00.23 /srv/env/bin/python3 -c from multiprocessing.spawn import spawn_main; spawn_main(tracker_fd=6, pipe_handle=24) --multiprocessing-fork

我在 ubuntu18.04 上使用带有 python3.8.0 的 virtualenv

$ python3 --version
Python 3.8.0

这种创建子进程的方法有什么问题?我需要创建很多需要轻量级的子进程,我最初认为使用 mp 的生成方法可以做到这一点,但它似乎不起作用。

@rmrmg的回答具有误导性。

是的,Spawn 会复制全局变量,但它不会复制受__name__=='__main__'范围保护的内存。Spawn 实质上是导入您当前的模块,当它执行此导入时,__name__=='__main__'块不会激活。这是__name__=='__main__'的重点(保护执行代码,使其不会在导入时运行(。

现在,关于为什么您的内存使用情况在进程中相似,那是因为您的dont_inherit由 500*100 整数组成,相当于 4*500*100 = 200000 字节 = 200 KB。您的子进程确实没有您的dont_inherit对象,节省的内存太小了,您甚至无法从运行top中检测到它。

将来,您应该尝试直接访问这些类型的对象,以便确认它们是否存在。

import multiprocessing  as mp
import numpy as np
def foo(x):
global dont_inherit
print(dont_inherit)
if __name__ == "__main__":
mp.set_start_method('spawn')
dont_inherit = np.ones((500, 100))
for x in range(3):
mp.Process(target=foo, args=(x,)).start()

如果运行此操作,您将看到打印语句将引发错误,因为不存在任何内容。

您还可以将dont_inherit变量增大几个数量级,以便实际查看内存使用情况。

简短回答:spawn 也复制全局变量,所以你应该:

  1. 首先创建流程,然后创建dont_inherit。我认为这更优雅,但可能并不总是可能的;或
  2. 在每个子流程中,del dont_inherit。创建子进程后,内存中只有一个dont_inherit副本(至少在 Linux 上,写入时复制效果很好(,因此删除子进程"引用"dont_inherit相当便宜且快速。

这里有一些更长的故事: 我不确定ps到底测量什么,所以我认为最好使用总内存使用量(例如使用htop(

import multiprocessing as mp
ctx = mp.get_context('spawn') #or fork, both work the same
q = ctx.Queue()
def proc(q):
while True:
msg = q.get()
print("Q", msg)
longlist = [ x for x in range(60_000_000) ]
#additional 2.3GB in RAM
p = ctx.Process(target=proc, args=(q,))
p.start()
#no change in memory usage 
for i in range( len(longlist) ):
longlist[i] = longlist[i]+1 #memory usage growing 
# when for is ended you have additional 2.3GB in RAM (now ~4.6GB is used)
# because you have original longlist in subprocess 
# and modified longlist in main processs

低于相同,但在子进程中具有 del 全局变量

import multiprocessing as mp
ctx = mp.get_context('spawn') #or fork, both work the same
q = ctx.Queue()
def proc(q):
global longlist
del longlist
while True:
msg = q.get()
print("Q", msg)
longlist = [ x for x in range(60_000_000) ]
#additional 2.3GB in RAM
p = ctx.Process(target=proc, args=(q,))
p.start()
#no change in memory usage 
for i in range( len(longlist) ):
longlist[i] = longlist[i]+1 #no change in memory usage
# in this point total memory usage is still ~2.3GB

最新更新