3

我正在通过 Python 执行一个外部程序。我想知道调用外部程序的最佳选择是使用subprocess.Popen()还是使用subprocess.call(). 此外,我需要测量经过的时间、外部程序使用的内存量和 CPU。我听说过psutil,但我真的不知道该选择哪个。

4

1 回答 1

10

我还需要测量 extern 程序使用的经过时间、内存量和 cpu

(我将假设您只需要在您的平台的rusage.以某种特定于平台的方式可用(读取 Linux 的proc文件系统,或调用 AIX 的监视器 API 或其他方式),你几乎不能用 stdlib 做到这一点,psutil答案是唯一的。)

subprocess库封装了调用fork,然后是execv子级中的waitpid-family 函数和父级中的 -family 函数。(您可以通过从源开始call并从那里追踪到其他调用来看到这一点。)

不幸的是,从孩子那里获取资源使用情况的简单方法是调用wait3or wait4,而不是waitor 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>

于 2014-10-20T22:36:01.957 回答