一个用于监控Python程序内存使用的模块
项目描述
内存分析器
注意: 此软件包不再积极维护。我不会积极回复问题。如果您想志愿维护它,请通过f@bianp.net联系我
这是一个用于监控进程内存使用以及Python程序逐行内存使用的python模块。这是一个纯Python模块,它依赖于psutil模块。
安装
通过pip安装
$ pip install -U memory_profiler
该软件包也可在conda-forge上找到。
要从源安装,请下载软件包,提取并输入
$ pip install .
快速入门
使用mprof生成可执行文件的完整内存使用报告并绘制它。
mprof run executable
mprof plot
图表可能如下所示
用法
逐行内存使用
逐行内存使用模式与line_profiler的使用方式类似:首先使用@profile装饰器装饰你想要分析的功能,然后使用特殊脚本(在本例中为Python解释器的特定参数)运行脚本。
以下示例中,我们创建了一个简单的函数my_func,该函数分配列表a和b,然后删除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)类似于这些
或者,使用mprof plot --flame(函数和时间戳名称将显示在悬停时)
关于这些功能的讨论可以在这里找到。
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绘制的图表,类似于以下图表
您可以将include-children和multiprocess标志结合起来,显示程序的总内存以及每个子进程单独的内存。如果直接使用API,请注意,memory_usage的返回值将包括子进程内存,与主进程内存一起作为嵌套列表。
绘图设置
默认情况下,命令行调用设置为图形标题。如果您想自定义它,可以使用-t选项手动设置图形标题。
mprof plot -t ‘记录的内存使用’
您还可以使用n标志隐藏函数时间戳,例如
mprof plot -n
使用s标志可以绘制趋势线及其数值斜率,例如
mprof plot -s
-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 邮箱联系我
开发
最新源代码可在 github 上获取
使用 memory_profiler 的项目
PySpeedIT(使用 memory_profiler 的简略版本)
pydio-sync(在 memory_profiler 之上使用自定义包装器)
许可证
BSD 许可证,请参阅文件 COPYING 获取全文。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。