измерять прошедшее время, объем памяти и процессор, используемый внешней программой

Я выполняю внешнюю программу через Python. Я хочу знать, что является лучшим выбором для вызова внешней программы, с помощью subprocess.Popen() или с помощью subprocess.call(). Кроме того, мне нужно измерить прошедшее время, объем памяти и ЦП, используемые внешней программой. Я слышал о psutil, но не знаю, что выбрать.


person C. Porto    schedule 20.10.2014    source источник
comment
subprocess.call — это тонкая оболочка вокруг subprocess.Popen. Взглянув на источник, вы должны понять различия.   -  person mgilson    schedule 21.10.2014


Ответы (1)


также мне нужно измерить прошедшее время, объем памяти и процессор, используемый внешней программой

(Я предполагаю, что вам нужна только информация, доступная в rusage вашей платформы. И , так как в Windows такого вообще нет, я также предполагаю, что Windows вам безразлична. API-интерфейсы мониторинга или что-то еще), вы практически не можете сделать это с помощью stdlib, и ответ psutil является единственным.)

Библиотека subprocess завершает вызов fork, затем execv-family в дочернем элементе и waitpid — семейная функция в родительском элементе. (Вы можете убедиться в этом, начав с исходного кода до call и отслеживание других звонков оттуда.)

К сожалению, простой способ узнать об использовании ресурсов от дочернего элемента — это вызвать wait3 или wait4, а не wait или waitpid. Так что subprocess приближает вас к тому, чего вы хотите, но не совсем туда.

Но у вас есть несколько вариантов:

  • Если у вас есть только один дочерний процесс, getrusage(RUSAGE_CHILDREN) — это все, что вам нужно. .
  • Вы можете запустить процесс, а затем использовать psutil (или код для конкретной платформы), чтобы получить информацию о ресурсах от proc.pid, прежде чем собирать дочерний элемент.
  • Вы можете использовать psutil для всего, оставив subprocess позади.
  • Вы можете создать подкласс subprocess.Popen, чтобы переопределить его метод wait.

Последнее намного проще, чем кажется. Если вы посмотрите на исходный код, то увидите, что os.waitpid вызывается только в 3 местах, и только одно из них будет влиять на ваш код; Я думаю, что это в _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 и так далее…

person abarnert    schedule 20.10.2014
comment
Спасибо за хорошо развернутый ответ! Я использую подход psutil, но когда я попытался запустить файл file.py, трассировка привела к следующему: psutil.NoSuchProcess: процесс больше не существует. Это исходит из последнего отпечатка. Что я могу сделать? - person C. Porto; 21.10.2014
comment
@C.Porto: Хорошо, некоторые функции psutil нельзя использовать после того, как вы пожинаете плоды процесса, некоторые — можно, и список зависит от платформы. Именно поэтому гибридное решение (используйте subprocess для запуска вещей, используйте psutil для их измерения) является худшим из вариантов… Если вам не нужно работать с psutil 1.x и psutil.Popen имеет достаточную функциональность, попробуйте. Я считаю, что он дает вам подкласс psutil.Process и что вы гарантированно сможете получить доступ к его материалам между вызовами communicate и wait, но я могу ошибаться. - person abarnert; 21.10.2014
comment
По-видимому, после того, как процесс будет собран, мне действительно не разрешено просматривать информацию о процессе (я на Mac OS X), даже с помощью psutil.Popen, знаете ли вы, есть ли другой способ измерения памяти и ЦП (например, прямо перед процесс пожинается)? - person C. Porto; 21.10.2014
comment
@C.Porto: вам нужно заблокировать, пока он не закончится, но на самом деле не звонить wait. Как я объяснил в своем предыдущем комментарии, я думаю, что класс psutil.Process (в версии 2.0+) позволяет вам вызывать communicate, а затем wait, но класс subprocess.Process определенно этого не делает (потому что communicate имеет неявный wait в нем). Если я неправильно помню, возможно, есть другой вариант. - person abarnert; 21.10.2014