如何使用Python的multiprocessing和memory_profiler分析多个子进程?

21
我有一个实用程序,使用Python的multiprocessing模块生成多个工作进程,并且我希望能够通过出色的memory_profiler实用程序跟踪它们的内存使用情况,该实用程序可以完全满足我的要求 - 特别是随时间对内存使用情况进行取样和绘制最终结果(对于此问题,我不关心逐行内存分析)。为了设置该问题,我创建了一个简化版的脚本,其中包含一个与memory_profiler库中给出的示例类似的分配内存的工作者函数。该工作者如下:
import time

X6 = 10 ** 6
X7 = 10 ** 7

def worker(num, wait, amt=X6):
    """
    A function that allocates memory over time.
    """
    frame = []

    for idx in range(num):
        frame.extend([1] * amt)
        time.sleep(wait)

    del frame

给定以下4个工人的顺序工作:

if __name__ == '__main__':
    worker(5, 5, X6)
    worker(5, 2, X7)
    worker(5, 5, X6)
    worker(5, 2, X7)

运行 mprof 可执行文件来对我的脚本进行分析,让每个工作进程一个接一个地运行,需要70秒。脚本的运行方式如下:

$ mprof run python myscript.py

产生以下内存使用图表:

Sequential Memory Generating Workers

拥有这些工人与multiprocessing并行执行意味着脚本将以最慢的工人(25秒)的速度完成。该脚本如下:
import multiprocessing as mp

if __name__ == '__main__':
    pool    = mp.Pool(processes=4)
    tasks   = [
        pool.apply_async(worker, args) for args in
        [(5, 5, X6), (5, 2, X7), (5, 5, X6), (5, 2, X7)]
    ]

    results = [p.get() for p in tasks]

记忆分析器确实可以工作,或者至少在使用 mprof 时没有错误,但结果有些奇怪:

enter image description here

快速查看活动监视器可以发现,实际上有6个Python进程,一个用于mprof,一个用于python myscript.py,然后每个工作子进程都有一个。看起来mprof只测量了python myscript.py进程的内存使用情况。

Python Processes in Activity Monitor

“memory_profiler”库高度可定制化,我非常有信心能够使用该库捕获每个进程的内存,并可能将其写入单独的日志文件中。只是我不确定从哪里开始或如何处理这种级别的定制化。
编辑
阅读了“mprof”脚本后,我发现“-C”标志可以汇总所有子(分叉)进程的内存使用情况。这导致了以下改进的图表:

Multiprocessing Workers with Include Children Flag

但我想要的是每个子进程随时间变化的内存使用情况,以便我可以在同一张图上绘制所有工作进程(和主进程)。我的想法是将每个子进程的 memory_usage 写入不同的日志文件,然后进行可视化。


如果有人感兴趣,这个问题正在GitHub上与开发人员讨论,网址为https://github.com/fabianp/memory_profiler/issues/118。 - bbengfort
1个回答

22

截至今日,内存分析库新增了一个功能,可以实现这一点。如果您需要此功能,请先按以下方式更新memory_profiler:

$ pip install -U memory_profiler 

这应该安装了 v0.44 版本的内存分析器。为了检查是否正确,请在运行操作上使用帮助命令:

mprof run --help
Usage: mprof run [options]

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  --python              Activates extra features when the profiling executable
                        is a Python program (currently: function
                        timestamping.)
  --nopython            Disables extra features when the profiled executable
                        is a Python program (currently: function
                        timestamping.)
  -T INTERVAL, --interval=INTERVAL
                        Sampling period (in seconds), defaults to 0.1
  -C, --include-children
                        Monitors forked processes as well (sum up all process
                        memory)
  -M, --multiprocess    Monitors forked processes creating individual plots
                        for each child

如果您看到-M标志,那么就可以开始了!

然后您可以按照以下方式运行您的脚本:

$ mprof run -M python myscript.py
$ mprof plot 

你应该得到一个类似于这样的图形:

跟踪单个子进程的mprof

请注意,如果您同时使用--include-children标志,则主进程内存将是所有子进程和主进程的总内存使用量,这也是一个有用的绘图。

在这种模式下启用时间戳和@profile装饰器怎么样?这是可能的吗? - petroslamb
我不确定你所说的启用时间戳是什么意思?我认为使用@profile装饰器应该可以实现这一点,它使用相同的参数。 - bbengfort
很遗憾我无法让它工作。请看 https://github.com/fabianp/memory_profiler/issues/148 - petroslamb
啊,我看到问题了——顺便说一句,那是一个非常好的错误报告。不幸的是,在处理不同类型的多进程分析时,腌菜问题随处可见。对此感到抱歉。 - bbengfort
1
有没有办法为子进程命名? child-n 实际上并不具有说明性 :) - Alon Gouldman
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接