如何使用Python中的psutil获取程序的最大内存使用量

3
我正在使用以下代码来获取程序的最大内存使用情况。
    import os, subprocess , psutil
    def mem(cmd):
        try:
            with open('in.txt','r') as infile, open('out.txt', 'w') as outfile:
                p=psutil.Popen("./"+cmd,shell=False,stdin=infile,stdout = outfile)
            print p.memory_info()
        except Exception:
             print "Error"
    cmd=raw_input()
    mem(cmd)

问题在于程序的初始运行时,内存使用输出为(0,0),但随后会显示正确的输出。我不知道这是为什么。对于一些程序,例如C++中的Hello World程序,输出为pmem(rss=4096, vms=315392),大约为0.3M(我认为输出单位为字节),但在ideone.com上运行Hello World程序的输出为~3M。为什么会有这种差异?cmd是可执行文件的名称。命令print subprocess.check_output(['ps', 'v', '-p', str(p.pid)])的输出结果如下:PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND 16150 pts/16 Z+ 0:00 0 0 0 0 0.0 [a.out] <defunct>以下是我的一个C++示例程序:
`int a[1000000];
int main()
{
    return 0;
}`

有时会返回 pmem(rss=4096, vms=4313088),有时会返回 pmem(rss=0,vms=0)。


该内容涉及 IT 技术。

你能用已知的程序(例如 ["python", "-c", "pass"])重现它吗?你确定你没有抑制异常,即 Popen() 成功启动了吗? - jfs
如果stderr=PIPE,那么你应该从p.stderr读取,否则如果它在stderr上生成足够的输出以填充相应的操作系统管道缓冲区,则进程可能会永远阻塞。 - jfs
@J.F.Sebastian 移除了 stderr。 - user2179293
print subprocess.check_output(['ps', 'v', '-p', str(p.pid)]) 显示什么? - jfs
@J.F.Sebastian,我添加了异常处理。有时输出是(0,0),但没有异常。 - user2179293
@J.F.Sebastian,请查看更新后的问题。 - user2179293
2个回答

3
问题在于psutils从/proc文件系统中快速获取快照,您可以在源代码中看到。当您运行“hello world”示例时,在某些情况下,它会在Python读取/proc中的值之前完成。一旦进程完成,它实际上不再消耗任何RAM。您可以通过strace确认这一点。
open("/proc/13420/statm", O_RDONLY)     = 3
read(3, "0 0 0 0 0 0 0\n", 1024)        = 14

如果您修改示例,使用类似sleep的东西,您会注意到psutils始终返回内存使用情况。
#include <iostream>
#include <unistd.h>

int main()
{
  std::cout << "Hello World.. sleeping!";
  sleep(3);
}

您的Python脚本输出...

a.out
meminfo(rss=286720, vms=12931072)

一种简单的实现你想要做的事情的方法是使用/usr/bin/time命令,在大多数平台上,它将为您提供启动的进程的平均总内存使用情况,或者像J.F Sebastian建议的那样使用valgrind...当我正在研究和测试我的答案时,他发布了帖子 :)
Hello World.. sleeping!0.00user 0.00system 0:03.00elapsed 0%CPU     
(0avgtext+0avgdata 1144maxresident)k
0inputs+0outputs (0major+348minor)pagefaults 0swaps

2
/usr/bin/time -f %M <command> 将打印“进程在其生命周期内的最大驻留集大小(以千字节为单位)”。 - jfs
为什么我运行程序时rss大小如此之低。我的最大rss大小是3292KB,而实际内存使用量约为49MB的程序?我的系统有问题吗? - user2179293

2
"" 表示子进程是一个僵尸进程(已经死亡,但其状态尚未被父进程(p.poll() 或 p.wait())读取)。似乎对于这样的进程,psutil 和 ps 都显示 RSS 为零。
结果取决于子进程是否比调用 p.memory_info() 更早退出。这是一场竞赛。如果在 C++ 程序中添加延迟,则 p.memory_info() 可能会在子进程退出之前被调用,并且应该会得到非零结果。

问题在于我可以让任意程序进行评估。语言也不固定。难道没有解决这个问题的优雅方法吗?

您可能需要操作系统支持,以保存关于子进程的内存使用情况的信息,即使在其退出后仍然如此。或者,您可以使用内存分析器(例如 valgrind)运行程序并阅读其结果。要收集结果:
$ valgrind --tool=massif cmd arg1 arg2 

您可以使用ms_print来查看结果:
$ ms_print massif.out.* | less

或者图形用户界面 Massif-Visualizer

@mdadm 建议 更简单的解决方案:time 命令

from subprocess import Popen, PIPE

p = Popen(['time', '-f', '%M'] + args, stderr=PIPE)
ru_maxrss = int(p.communicate()[1])
print("Maximum rss %d KB" % ru_maxrss)

GNU time使用wait3()来填充资源使用信息(如果可用)。它可以在Python中调用:
import os
from subprocess import Popen

p = Popen(args)
ru = os.wait4(p.pid, 0)[2]
print("Maximum rss %d KB" % ru.ru_maxrss)

我已经将psutil.Process.memory_info(rss)返回的最大值与os.wait4返回的ru_maxrss值以及valgrind --tool=massif报告的最大总内存进行了比较:它们相似
另请参见:

我尝试使用/usr/bin/time -v ./a.out<int.txt>out.txt,但驻留集大小仍然不准确,非常低。 - user2179293
我已经添加了基于os.wait4()的解决方案。它类似于您原来基于resource的解决方案。您是否将它们与psutilvalgrind的结果进行了比较? - jfs
我不知道如何使用valgrind,现在我观察到我的psutil结果显示rss = 4096,每当有一些非零输出时,都是一个恒定的值。为了计算内存,我使用了实际上是错误的vms。我的系统有什么问题吗?为了比较内存大小,我正在使用像ideone.com这样的在线编译器。 - user2179293
@user2179293:确保一下:p.memory_info()返回的是当前内存使用情况。这里是我用来找出最大内存使用情况的代码,使用了psutil:https://gist.github.com/zed/9859060 - jfs
你能检查一下 https://ideone.com/KK1rak 的内存使用情况吗?我在我的系统上使用了你的程序,输出结果是4Mb,而在 ideone 上是49Mb。 - user2179293
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接