如何使用Python多处理和内存分析器分析多个子流程?

如何使用Python多处理和内存分析器分析多个子流程?,python,python-multiprocessing,memory-profiling,Python,Python Multiprocessing,Memory Profiling,我有一个实用程序,它使用Python模块生成多个工作人员,我希望能够通过优秀的实用程序跟踪他们的内存使用情况,该实用程序可以完成我想要的一切—特别是随时间采样内存使用情况并绘制最终结果(我不关心这个问题的逐行内存分析) 为了设置这个问题,我创建了一个更简单的脚本版本,它有一个worker函数,分配的内存与memory\u profiler库中给出的类似。工作人员如下: import time X6 = 10 ** 6 X7 = 10 ** 7 def worker(num, wait, am

我有一个实用程序,它使用Python模块生成多个工作人员,我希望能够通过优秀的实用程序跟踪他们的内存使用情况,该实用程序可以完成我想要的一切—特别是随时间采样内存使用情况并绘制最终结果(我不关心这个问题的逐行内存分析)

为了设置这个问题,我创建了一个更简单的脚本版本,它有一个worker函数,分配的内存与
memory\u 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
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]
$ pip install -U memory_profiler 
$ mprof run -M python myscript.py
$ mprof plot 
考虑到连续4名工人的工作量,如下所示:

if __name__ == '__main__':
    worker(5, 5, X6)
    worker(5, 2, X7)
    worker(5, 5, X6)
    worker(5, 2, X7)
$ mprof run python myscript.py
运行
mprof
可执行文件来评测我的脚本需要70秒,让每个工作进程一个接一个地运行。脚本将按如下方式运行:

if __name__ == '__main__':
    worker(5, 5, X6)
    worker(5, 2, X7)
    worker(5, 5, X6)
    worker(5, 2, X7)
$ mprof run python myscript.py
生成以下内存使用率图:

让这些worker与
多处理并行运行
意味着脚本将以最慢的worker(25秒)的速度完成。该脚本如下:

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
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]
$ pip install -U memory_profiler 
$ mprof run -M python myscript.py
$ mprof plot 
内存分析器确实可以工作,或者至少在使用
mprof
时没有错误,但结果有点奇怪:

快速查看Activity Monitor可以发现实际上有6个Python进程,一个用于
mprof
,一个用于
Python myscript.py
,然后每个辅助进程一个。似乎
mprof
只是测量
python myscript.py
进程的内存使用情况

memory\u profiler
库是高度可定制的,我相信我应该能够捕获每个进程的内存,并可能使用库本身将它们写入单独的日志文件。我只是不确定从何处开始,也不知道如何达到这种定制水平

编辑

在阅读了
mprof
脚本之后,我确实发现了
-C
标志,它总结了所有子(分叉)进程的内存使用情况。这将产生一个(大大改进的)图表,如下所示:

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
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]
$ pip install -U memory_profiler 
$ mprof run -M python myscript.py
$ mprof plot 


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

从今天起,内存探查器库中添加了一项新功能,可以实现这一点。如果需要此功能,请首先按如下方式更新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
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]
$ pip install -U memory_profiler 
$ mprof run -M python myscript.py
$ mprof plot 
这应该安装v0.44版本的内存探查器。要检查是否存在这种情况,请对运行操作使用help命令:

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
标志,那么您就可以开始了

然后,您可以按如下方式运行脚本:

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
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]
$ pip install -U memory_profiler 
$ mprof run -M python myscript.py
$ mprof plot 
你应该得到这样一个数字:


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

如果有人感兴趣,这个问题将在GitHub上与开发者讨论。特别感谢@fabian pedregosa帮助实现这一点!在这种模式下启用时间戳和
@profile
装饰器怎么样?可能吗?我不知道你说的启用时间戳是什么意思?我认为这应该可以通过
@profile
装饰器实现,它使用相同的参数。不幸的是,我无法让它工作。请看一下是否有一种方法可以为子进程命名
child-n
不是真正的解释:)