【问题标题】:Understand python multiprocessing child process memory usage with 'spawn' start method使用“spawn”启动方法了解 python 多处理子进程内存使用情况
【发布时间】:2020-05-28 13:04:48
【问题描述】:

我正在尝试使用多处理模块在 python 3.8.0 中创建一个子进程,而不继承父进程的内存。我为此使用了 spawn start 方法mp.set_start_method('spawn')。但是子进程的内存使用和父进程几乎一样。代码如下:sn-ps

我正在使用此处共享的代码来测试How can I restrict the scope of a multiprocessing process?

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 的 spawn 方法可以做到这一点,但它似乎不起作用。

【问题讨论】:

    标签: python-3.x multiprocessing python-multiprocessing ubuntu-18.04 spawn


    【解决方案1】:

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

    1. 先创建进程,然后再dont_inherit。我认为这更优雅,但可能并不总是可行;或
    2. 在每个子进程中删除 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
    

    【讨论】:

      【解决方案2】:

      @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 变量增大几个命令,以便实际查看内存使用情况。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-16
        • 2018-10-11
        • 2011-03-09
        • 2010-10-19
        • 2015-01-13
        • 2019-04-18
        • 2010-12-17
        • 2010-12-01
        相关资源
        最近更新 更多