我正在通过 Python 执行一个外部程序。我想知道调用外部程序的最佳选择是使用subprocess.Popen()
还是使用subprocess.call()
. 此外,我需要测量经过的时间、外部程序使用的内存量和 CPU。我听说过psutil
,但我真的不知道该选择哪个。
1 回答
我还需要测量 extern 程序使用的经过时间、内存量和 cpu
(我将假设您只需要在您的平台的rusage
.以某种特定于平台的方式可用(读取 Linux 的proc
文件系统,或调用 AIX 的监视器 API 或其他方式),你几乎不能用 stdlib 做到这一点,psutil
答案是唯一的。)
该subprocess
库封装了调用fork
,然后是execv
子级中的waitpid
-family 函数和父级中的 -family 函数。(您可以通过从源开始call
并从那里追踪到其他调用来看到这一点。)
不幸的是,从孩子那里获取资源使用情况的简单方法是调用wait3
or wait4
,而不是wait
or waitpid
。所以subprocess
让你疯狂地接近你想要的,但并不完全在那里。
但是你有几个选择:
- 如果您只有一个子进程,那么您只需要一个
getrusage(RUSAGE_CHILDREN)
。 - 您可以启动该过程,然后使用
psutil
(或特定于平台的代码)proc.pid
在收获孩子之前从那里获取资源信息。 - 你可以用
psutil
做一切,留下subprocess
。 - 您可以子类化
subprocess.Popen
以覆盖其wait
方法。
最后一个比听起来简单得多。如果您查看源代码,实际调用的地方只有 3 个,其中os.waitpid
只有一个会影响您的代码;我认为它是_try_wait
. 所以(未经测试):
class ResourcePopen(subprocess.Popen):
def _try_wait(self, wait_flags):
"""All callers to this function MUST hold self._waitpid_lock."""
try:
(pid, sts, res) = _eintr_retry_call(os.wait4, self.pid, wait_flags)
except OSError as e:
if e.errno != errno.ECHILD:
raise
# This happens if SIGCLD is set to be ignored or waiting
# for child processes has otherwise been disabled for our
# process. This child is dead, we can't get the status.
pid = self.pid
sts = 0
else:
self.rusage = res
return (pid, sts)
def resource_call(*popenargs, timeout=None, **kwargs):
"""Run command with arguments. Wait for command to complete or
timeout, then return the returncode attribute and resource usage.
The arguments are the same as for the Popen constructor. Example:
retcode, rusage = call(["ls", "-l"])
"""
with ResourcePopen(*popenargs, **kwargs) as p:
try:
retcode = p.wait(timeout=timeout)
return retcode, p.rusage
except:
p.kill()
p.wait()
raise
现在:
retcode, rusage = resource_call(['spam', 'eggs'])
print('spam used {}s of system time'.format(rusage.ru_stime))
将其与使用混合psutil
(在许多平台上以这种方式使用时甚至不起作用......):
p = subprocess.Popen(['spam', 'eggs'])
ps = psutil.Process(p.pid)
p.wait()
print('spam used {}s of system time'.format(ps.cpu_times().system))
当然,后者并不是没有充分的理由更复杂,它更复杂是因为它更强大、更灵活;你还可以获取各种rusage
不包括在内的数据,并且可以在进程运行时每秒获取信息,而不是等到它完成,你可以在 Windows 上使用它,等等……</p>