Skip to main content

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

项目描述

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

内存分析器

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

安装

通过 pip 安装:

$ pip install -U memory_profiler

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

要从源代码安装,请下载包,解压缩并键入:

$ python setup.py install

用法

逐行内存使用

逐行内存使用模式的使用方式与 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 解释器的代码以加载 memory_profiler 模块并打印到 stdout 逐行分析。如果文件名是 example.py,这将导致:

$ python -m memory_profiler example.py

输出将如下:

Line #    Mem usage  Increment   Line Contents
==============================================
     3                           @profile
     4      5.97 MB    0.00 MB   def my_func():
     5     13.61 MB    7.64 MB       a = [1] * (10 ** 6)
     6    166.20 MB  152.59 MB       b = [2] * (2 * 10 ** 7)
     7     13.61 MB -152.59 MB       del b
     8     13.61 MB    0.00 MB       return a

第一列表示已分析代码的行号,第二列(Mem 使用情况)表示执行该行后 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

如果在命令行中使用-m memory_profiler调用带有装饰器 @profile 的 python 脚本,则会忽略精度参数。

基于时间的内存使用

有时,将完整的内存使用报告作为外部进程(无论是否是 Python 脚本)的时间函数(不是逐行)很有用。在这种情况下,可执行的mprof可能很有用。像这样使用它:

mprof run <executable>
mprof plot

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

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

在 Python 脚本的情况下,使用前面的命令不会为您提供有关在给定时间执行哪个函数的任何信息。根据具体情况,可能很难识别导致最高内存使用的代码部分。

配置文件装饰器添加到函数并运行 Python 脚本

mprof 运行 <脚本>

将在进入/离开分析功能时记录时间戳。跑步

mprof 图

之后将绘制结果,使图(使用 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实用程序提供两种机制来跟踪子进程的使用情况:将所有子进程的内存与父进程的使用情况相加,并跟踪每个子进程。

要创建结合所有子项和父项的内存使用情况的报告,请在配置文件装饰器中使用include_children标志或作为mprof的命令行参数:

mprof run --include-children <script>

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

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 绘图 -n

设置调试器断点

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

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

一旦代码在修饰函数中使用超过 100 MB,将运行my_script.py并进入 pdb 调试器。

API

memory_profiler 公开了许多要在第三方代码中使用的函数。

memory_usage(proc=-1, interval=.1, timeout=None)返回一个时间间隔内的内存使用情况。第一个参数proc表示应该监控的内容。这可以是进程的 PID(不一定是 Python 程序)、包含要评估的一些 Python 代码的字符串或包含要评估为f(*args )的函数及其参数的元组(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 以 0.2 秒的时间间隔获取当前进程在 1 秒内的内存消耗。作为 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

通过记录器模块报告:

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

只需使用内存分析器模块的 LogFile,就可以将输出重定向到记录器模块。

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

定制报告:

在运行 memory_profiler 时将所有内容发送到日志文件可能很麻烦,并且可以通过将 True 传递给 reportIncrementFlag 来仅选择具有增量的条目,其中 reportIncrementFlag 是内存分析器模块的 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

有关更多详细信息,请参阅魔法的文档字符串。

对于 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)

经常问的问题

  • 问:结果有多准确?

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

  • 问:windows下可以用吗?

  • A:是的,感谢 psutil模块。

支持、错误和愿望清单

如需支持,请询问有关堆栈溢出的问题并添加*memory-profiling* 标签。将问题、建议等发送到github 的问题跟踪器

如果您对开发有任何疑问,可以直接通过f @ bianp给我发电子邮件

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

发展

最新资源可从 github 获得:

https://github.com/pythonprofilers/memory_profiler

使用 memory_profiler 的项目

长凳

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增加了对跟踪单个子进程的使用并绘制它们的支持。

Muhammad Haseeb Tariq修复了问题 #152,它使整个解释器挂在启动异常的函数上。

胡安·路易斯·卡诺 (Juan Luis Cano ) 对基础设施进行了现代化改造并提供了各种帮助。

执照

BSD 许可证,请参阅文件复制以获取全文。

下载文件

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

源分布

cglacet-memory-profiler-0.57.0.tar.gz (36.5 kB 查看哈希

已上传 source