当前位置: 首页 > 科技观察

如何监控Python程序的内存使用情况

时间:2023-03-18 23:06:59 科技观察

当我们使用Python及其数据处理库套件(如panda、scikit-learn)进行大数据处理时,可能会占用大量的计算资源。如何监控程序的内存使用情况就显得尤为重要。1.要求操作系统跟踪内存使用情况的最简单方法是使用操作系统本身。您可以使用top来提供您在一段时间内使用过的资源的概览。或者,如果您想实时检查资源使用情况,可以使用ps命令:$ps-m-o%cpu,%mem,command%CPU%MEMCOMMAND23.47.2pythonanalyze_data.py0.00.0bashm标志指示ps使用大多数内存按进程顺序显示结果。o标志控制显示每个进程的哪些属性——在本例中,CPU使用百分比、系统内存消耗百分比以及执行进程的命令行。CPU百分比将一个完整的CPU内核计为100%使用率,因此如果您有一台4核机器,您可能会看到总共高达400%的CPU使用率。还有其他用于显示其他进程属性的输出选项,以及用于控制显示哪些进程的ps的其他标志。结合一些创造性的shell脚本,可以编写一个使用ps跟踪任务内存使用情况的监控脚本。2、tracemallocPython解释器的运行中有大量的钩子,可用于Python代码运行时的监控和自省。pdb使用这些钩子来提供调试;coverage还使用它们来提供测试覆盖率。tracemalloc模块也使用它们来提供内存使用情况的窗口。tracemalloc是Python3.4中添加的标准库模块,用于跟踪Python解释器分配的每个内存块。tracemalloc可以提供有关正在运行的Python进程中内存分配的非常细粒度的信息:{peak/10**6}MB")tracemalloc.stop()调用tracemplugin.start()启动跟踪过程。进行跟踪时,您可以询问有关分配的内容的详细信息;在这个例子中,我们只要求当前和峰值内存分配。调用tracemplugin.stop()将删除挂钩并清除所有已收集的痕迹。不过,这种详细程度是有代价的。tracemalloc将自身深入到正在运行的Python进程中——如您所料,这会带来性能损失。在我们的测试中,我们观察到在运行分析时使用tracemalloc速度降低了30%。这在分析单个进程时可能没问题,但在生产中,您真的不想仅仅为了监视内存使用而降低30%的性能。3.采样幸运的是,Python标准库提供了另一种观察内存使用情况的方法——资源模块。资源模块提供对程序分配的资源的基本控制,包括内存使用:调用importresourceusage=resource.getrusage(resource.RUSAGE_SELF).ru_maxrssgetrusage()返回程序使用的资源。常量RUSAGE_SELF表明我们只对这个进程使用的资源感兴趣,而不是它的子进程。返回的对象是一个包含一系列操作系统资源的结构,包括CPU时间、信号、上下文切换等;但出于我们的目的,我们对maxrss感兴趣-最大驻留集大小-这是进程当前在RAM中持有的内存量。但是,与tracemalloc模块不同,资源模块不会随时间跟踪使用情况——它只提供点采样。因此,我们需要实现一种方法来对一段时间内的内存使用情况进行采样。首先我们定义一个类来进行内存监控:importresourcefromtimeimportsleepclassMemoryMonitor:def__init__(self):self.keep_measuring=Truedefmeasure_usage(self):max_usage=0whileself.keep_measuring:maxmax_usage=max(max_usage,resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)sleep(0.1)returnmax_usage当在此类的实例上调用measure_usage()时,它会进入一个循环,每0.1秒测量一次内存使用情况。将跟踪内存使用量的任何增加,并在循环退出时返回最大内存分配。但是什么告诉循环退出呢?我们在哪里调用被监视的代码?我们在一个单独的线程中进行。fromconcurrent.futuresimportThreadPoolExecutorwithThreadPoolExecutor()asexecutor:monitor=MemoryMonitor()mem_thread=executor.submit(monitor.measure_usage)try:fn_thread=executor.submit(my_analysis_function)result=fn_threadus.result_()finally:monitoring.keep.age_measure()打印(f"Peakmemoryusage:{max_usage}")ThreadPoolExecutor提供了一个方便的方法来提交任务在线程中执行。我们向执行器提交两个任务-monitor和my_analysis_function(如果分析函数需要额外的参数,它们可以通过提交调用传入)。对fn_thread.result()的调用将阻塞,直到分析函数完成并获得其结果,此时我们可以告诉监视器停止并获得最大内存。try/finally模块确保如果分析函数抛出异常,内存线程仍然会被终止。使用这种方法,我们可以随着时间的推移有效地采样内存使用情况。大部分工作将在主分析线程中完成;但是每隔0.1秒,监视器线程就会唤醒,进行内存测量,如果内存使用量增加,则存储它,然后返回睡眠状态。英文原文:https://medium.com/survata-engineering-blog/monitoring-memory-usage-of-a-running-python-program-49f027e3d1ba