跳转到主要内容

一个用于监控Python程序内存使用的模块

项目描述

https://travis-ci.org/pythonprofilers/memory_profiler.svg?branch=master

内存分析器

注意: 此软件包不再积极维护。我不会积极回复问题。如果您想志愿维护它,请通过f@bianp.net联系我

这是一个用于监控进程内存使用以及Python程序逐行内存使用的python模块。这是一个纯Python模块,它依赖于psutil模块。

安装

通过pip安装

$ pip install -U memory_profiler

该软件包也可在conda-forge上找到。

要从源安装,请下载软件包,提取并输入

$ pip install .

快速入门

使用mprof生成可执行文件的完整内存使用报告并绘制它。

mprof run executable
mprof plot

图表可能如下所示

https://i.stack.imgur.com/ixCH4.png

用法

逐行内存使用

逐行内存使用模式与line_profiler的使用方式类似:首先使用@profile装饰器装饰你想要分析的功能,然后使用特殊脚本(在本例中为Python解释器的特定参数)运行脚本。

以下示例中,我们创建了一个简单的函数my_func,该函数分配列表ab,然后删除b

@profile
def my_func():
    a = [1] * (10 ** 6)
    b = [2] * (2 * 10 ** 7)
    del b
    return a

if __name__ == '__main__':
    my_func()

通过将选项-m memory_profiler传递给Python解释器来加载内存分析器模块,并将逐行分析打印到标准输出。如果文件名为example.py,则会得到以下结果:

$ python -m memory_profiler example.py

输出将随后展示

Line #    Mem usage    Increment  Occurrences   Line Contents
============================================================
     3   38.816 MiB   38.816 MiB           1   @profile
     4                                         def my_func():
     5   46.492 MiB    7.676 MiB           1       a = [1] * (10 ** 6)
     6  199.117 MiB  152.625 MiB           1       b = [2] * (2 * 10 ** 7)
     7   46.629 MiB -152.488 MiB           1       del b
     8   46.629 MiB    0.000 MiB           1       return a

第一列代表已分析的代码行号,第二列(内存使用)代表执行该行后的Python解释器的内存使用量。第三列(增量)代表当前行与上一行之间的内存差异。最后一列(行内容)打印已分析的代码。

装饰器

还提供了一种函数装饰器。使用方法如下

from memory_profiler import profile

@profile
def my_func():
    a = [1] * (10 ** 6)
    b = [2] * (2 * 10 ** 7)
    del b
    return a

在这种情况下,可以在命令行中不指定-m memory_profiler来运行脚本。

在函数装饰器中,你可以将精度作为装饰器函数的参数指定。使用方法如下

from memory_profiler import profile

@profile(precision=4)
def my_func():
    a = [1] * (10 ** 6)
    b = [2] * (2 * 10 ** 7)
    del b
    return a

如果使用带有装饰器@profile的Python脚本通过命令行使用-m memory_profiler调用,则忽略precision参数。

基于时间的内存使用

有时,将完整内存使用报告作为时间(而不是逐行)的函数(无论是Python脚本还是其他外部进程)非常有用。在这种情况下,可执行文件mprof可能很有用。使用方法如下

mprof run <executable>
mprof plot

第一行运行可执行文件并记录内存使用情况,在当前目录中写入一个文件。完成后,可以使用第二行获得图表。记录的文件包含时间戳,允许同时保留多个配置文件。

可以使用-h标志获取每个mprof子命令的帮助,例如mprof run -h

在Python脚本的情况下,使用前面的命令不会给出关于在给定时间执行哪个函数的信息。根据情况,可能很难确定导致最高内存使用的代码部分。

profile装饰器添加到函数中(确保没有from memory_profiler import profile语句),并使用以下方式运行Python脚本:

mprof run –python python <script>

将记录进入/离开已分析函数的时间戳。运行

mprof plot

之后将绘制结果,生成的图表(使用matplotlib)类似于这些

https://camo.githubusercontent.com/3a584c7cfbae38c9220a755aa21b5ef926c1031d/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313930383631382f3836313332302f63623865376337382d663563632d313165322d386531652d3539373237623636663462322e706e67

或者,使用mprof plot --flame(函数和时间戳名称将显示在悬停时)

./images/flamegraph.png

关于这些功能的讨论可以在这里找到。

mprof的可用命令如下

  • mprof run:运行可执行文件,记录内存使用情况

  • mprof plot:绘制记录的内存使用情况(默认为最后一条记录)

  • mprof list:以用户友好的方式列出所有已记录的内存使用文件。

  • mprof clean:删除所有已记录的内存使用文件。

  • mprof rm:删除特定的已记录内存使用文件

跟踪分叉子进程

在多进程上下文中,主进程会创建子进程,其系统资源独立于父进程分配。这可能导致内存使用报告不准确,因为默认情况下只跟踪父进程。mprof实用程序提供了两种机制来跟踪子进程的使用:将所有子进程的内存加到父进程的使用上,以及单独跟踪每个子进程。

要创建一个结合所有子进程和父进程内存使用的报告,请在profile装饰器或作为对mprof的命令行参数使用include-children标志。

mprof run --include-children <script>

第二种方法独立于主进程跟踪每个子进程,通过索引将子行序列化到输出流。使用multiprocess标志并按如下方式绘图

mprof run --multiprocess <script>
mprof plot

这将创建一个使用matplotlib绘制的图表,类似于以下图表

https://cloud.githubusercontent.com/assets/745966/24075879/2e85b43a-0bfa-11e7-8dfe-654320dbd2ce.png

您可以将include-childrenmultiprocess标志结合起来,显示程序的总内存以及每个子进程单独的内存。如果直接使用API,请注意,memory_usage的返回值将包括子进程内存,与主进程内存一起作为嵌套列表。

绘图设置

默认情况下,命令行调用设置为图形标题。如果您想自定义它,可以使用-t选项手动设置图形标题。

mprof plot -t ‘记录的内存使用’

您还可以使用n标志隐藏函数时间戳,例如

mprof plot -n

使用s标志可以绘制趋势线及其数值斜率,例如

mprof plot -s

./images/trend_slope.png

-s开关的预期用途是在较长时间段内检查标签的数值斜率,

  • >0它可能意味着内存泄漏。

  • ~0如果为0或接近0,则内存使用可能被认为是稳定的。

  • <0应根据预期的进程内存使用模式来解释,也可能意味着采样周期太小。

趋势线用于说明目的,并作为(非常)细的虚线绘制。

设置调试器断点

可以设置基于内存使用的断点。也就是说,您可以指定一个阈值,一旦程序使用的内存超过阈值,程序就会停止执行并运行到pdb调试器中。要使用它,您必须像上一节中那样用@profile装饰函数,然后使用选项-m memory_profiler --pdb-mmem=X运行您的脚本,其中X是一个表示MB内存阈值的数字。例如

$ python -m memory_profiler --pdb-mmem=100 my_script.py

将运行my_script.py,一旦装饰的函数中使用了超过100 MB的内存,就会进入pdb调试器。

API

memory_profiler公开了一些函数,用于在第三方代码中使用。

memory_usage(proc=-1, interval=.1, timeout=None) 函数返回时间间隔内的内存使用情况。第一个参数 proc 代表要监控的内容。这可以是进程的 PID(不一定是 Python 程序),包含一些要评估的 Python 代码的字符串,或者一个包含要评估的函数及其参数的元组 (f, args, kw),其格式为 f(*args, **kw)。例如,

>>> from memory_profiler import memory_usage
>>> mem_usage = memory_usage(-1, interval=.2, timeout=1)
>>> print(mem_usage)
    [7.296875, 7.296875, 7.296875, 7.296875, 7.296875]

这里我告诉 memory_profiler 在 1 秒内以 0.2 秒的时间间隔获取当前进程的内存消耗。我给它提供的 PID 是 -1,这是一个特殊数字(PID 通常为正数),表示当前进程,即我正在获取当前 Python 解释器的内存使用情况。因此,我从一个普通的 Python 解释器中获取了大约 7MB 的内存使用情况。如果我在 IPython(控制台)上做同样的事情,我得到 29MB,如果我在 IPython 笔记本上做同样的事情,它会增加到 44MB。

如果您想获取 Python 函数的内存消耗,那么您应该在元组 (f, args, kw) 中指定函数及其参数。例如

>>> # define a simple function
>>> def f(a, n=100):
    ...     import time
    ...     time.sleep(2)
    ...     b = [a] * n
    ...     time.sleep(1)
    ...     return b
    ...
>>> from memory_profiler import memory_usage
>>> memory_usage((f, (1,), {'n' : int(1e6)}))

这将执行代码 f(1, n=int(1e6)) 并返回执行过程中的内存消耗。

报告

可以通过将 IO 流作为参数传递给装饰器来将输出重定向到日志文件,例如 @profile(stream=fp)

>>> fp=open('memory_profiler.log','w+')
>>> @profile(stream=fp)
>>> def my_func():
    ...     a = [1] * (10 ** 6)
    ...     b = [2] * (2 * 10 ** 7)
    ...     del b
    ...     return a

详细信息请参阅:examples/reporting_file.py

通过 logger 模块进行报告

有时使用 logger 模块会非常方便,尤其是在我们需要使用 RotatingFileHandler 时。

可以通过利用 memory_profiler 模块的 LogFile 简单地将输出重定向到 logger 模块。

>>> from memory_profiler import LogFile
>>> import sys
>>> sys.stdout = LogFile('memory_profile_log')

自定义报告

在运行 memory_profiler 时,将所有内容发送到日志文件可能会很麻烦,您可以通过将 reportIncrementFlag 设置为 True 来选择只有增量条目,其中 reportIncrementFlag 是 memory_profiler 模块的 LogFile 类的参数。

>>> from memory_profiler import LogFile
>>> import sys
>>> sys.stdout = LogFile('memory_profile_log', reportIncrementFlag=False)

详细信息请参阅:examples/reporting_logger.py

IPython 集成

安装模块后,如果使用 IPython,您可以使用 %mprun%%mprun%memit%%memit 魔法。

对于 IPython 0.11+,您可以直接将其作为扩展使用,使用 %load_ext memory_profiler

要激活它,每次启动 IPython 时,请编辑您的 IPython 配置文件 ~/.ipython/profile_default/ipython_config.py,以注册扩展,如下所示(如果您已经有其他扩展,只需将此扩展添加到列表中)

c.InteractiveShellApp.extensions = [
    'memory_profiler',
]

(如果配置文件尚不存在,请在终端中运行 ipython profile create

然后可以直接从 IPython 使用 %mprun%%mprun 魔法命令来获取逐行报告。在这种情况下,您可以跳过 @profile 装饰器,而是使用 -f 参数,如下所示。但是请注意,my_func 函数必须在文件中定义(不能在 Python 解释器中交互式定义)

In [1]: from example import my_func, my_func_2

In [2]: %mprun -f my_func my_func()

或单元格模式

In [3]: %%mprun -f my_func -f my_func_2
   ...: my_func()
   ...: my_func_2()

我们定义的另一个有用的魔法是 %memit,它与 %timeit 类似。它可以如下使用

In [1]: %memit range(10000)
peak memory: 21.42 MiB, increment: 0.41 MiB

In [2]: %memit range(1000000)
peak memory: 52.10 MiB, increment: 31.08 MiB

或单元格模式(带有设置代码)

In [3]: %%memit l=range(1000000)
   ...: len(l)
   ...:
peak memory: 52.14 MiB, increment: 0.08 MiB

有关更多详细信息,请参阅魔法的 docstrings。

对于 IPython 0.10,您可以通过编辑 IPython 配置文件 ~/.ipython/ipy_user_conf.py 来安装它,添加以下行

# These two lines are standard and probably already there.
import IPython.ipapi
ip = IPython.ipapi.get()

# These two are the important ones.
import memory_profiler
memory_profiler.load_ipython_extension(ip)

内存跟踪后端

memory_profiler 支持不同的内存跟踪后端,包括:‘psutil’,‘psutil_pss’,‘psutil_uss’,‘posix’,‘tracemalloc’。如果没有指定特定的后端,则默认使用“psutil”,它测量 RSS 即“常驻集大小”。在某些情况下(尤其是跟踪子进程时),RSS 可能会高估内存使用(请参见 example/example_psutil_memory_full_info.py 中的示例)。有关“psutil_pss”(测量 PSS)和“psutil_uss”的更多信息,请参阅:[https://psutil.readthedocs.io/en/latest/index.html?highlight=memory_info#psutil.Process.memory_full_info](https://psutil.readthedocs.io/en/latest/index.html?highlight=memory_info#psutil.Process.memory_full_info)

目前,可以通过 CLI 设置后端

$ python -m memory_profiler –backend psutil my_script.py

并且通过 API 公开

>>> from memory_profiler import memory_usage
>>> mem_usage = memory_usage(-1, interval=.2, timeout=1, backend="psutil")

常见问题解答

  • Q:结果有多准确?

  • A:此模块通过查询操作系统内核来获取当前进程已分配的内存量,这可能与 Python 解释器实际使用的内存量略有不同。此外,由于 Python 中垃圾收集器的工作方式,结果可能在平台之间甚至在不同运行之间有所不同。

  • Q:它能在 Windows 下运行吗?

  • A:是的,多亏了 psutil 模块。

支持、错误和愿望列表

有关支持,请在 stack overflow 上提出您的问题,并添加 *memory-profiling* 标签。将问题、建议等发送到 github 的问题跟踪器

如果您有关开发方面的问题,可以直接通过 f@bianp.net 邮箱联系我

http://fa.bianp.net/static/tux_memory_small.png

开发

最新源代码可在 github 上获取

https://github.com/pythonprofilers/memory_profiler

使用 memory_profiler 的项目

Benchy

IPython 内存使用情况

PySpeedIT(使用 memory_profiler 的简略版本)

pydio-sync(在 memory_profiler 之上使用自定义包装器)

作者

本模块由 Fabian PedregosaPhilippe Gervais 编写,受 Robert Kern 的 line profiler 的启发。

Tom 通过 psutil 模块添加了 Windows 支持,并通过速度改进。

Victor 添加了 Python3 支持、错误修复和一般清理。

Vlad Niculae 添加了 %mprun%memit IPython 魔术。

Thomas Kluyver 添加了 IPython 扩展。

Sagar UDAY KUMAR 添加了报告生成功能和示例。

Dmitriy NovozhilovSergei Lebedev 添加了对 tracemalloc 的支持。

Benjamin Bengfort 添加了对跟踪单个子进程的使用情况并进行绘图的 support。

Muhammad Haseeb Tariq 修复了导致整个解释器在抛出异常的函数上挂起的 issue #152。

Juan Luis Cano 现代化了基础设施,并在多个方面提供了帮助。

Martin Becker 通过 psutil 后端添加了 PSS 和 USS 跟踪。

许可证

BSD 许可证,请参阅文件 COPYING 获取全文。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分发

memory_profiler-0.61.0.tar.gz (35.9 kB 查看哈希值)

上传时间 源代码

构建分发

memory_profiler-0.61.0-py3-none-any.whl (31.8 kB 查看哈希值)

上传时间 Python 3

支持者