使用/ usr / bin / time的Python子进程:如何捕获时序信息但忽略所有其他输出?

时间:2015-02-14 21:24:44

标签: python python-2.7 time stdout stderr

我正在尝试测量通过子进程调用的可执行程序的执行时间(以秒为单位)。我不希望发出可执行文件的输出(stderr或stdout)。

我已经尝试了timeit和资源库,但是没有准确地捕获进程的时间,看起来它只捕获了Python工作线程中的时序。

以下尝试将丢失stderr重定向的时间信息b / c。但是,如果没有stderr重定向,将发出命令'f_cmd'stderr输出。

def doWithTiming(f_cmd):
    DEVNULL = open(os.devnull, 'w')
    return subprocess.check_output([ "/usr/bin/time", "--format=%e seconds"] + f_cmd.split(), stderr=DEVNULL)

如何忽略f_cmd的所有输出但保留/ usr / bin / time的输出?

2 个答案:

答案 0 :(得分:6)

%e /usr/bin/time format is

  

过程使用的实际(挂钟)时间,以秒为单位。

使用抑制的stdout / stderr运行子进程并获取已用时间:

#!/usr/bin/env python
import os
import time
from subprocess import check_call, STDOUT

DEVNULL = open(os.devnull, 'wb', 0)

start = time.time()
check_call(['sleep', '1'], stdout=DEVNULL, stderr=STDOUT)
print("{:.3f} seconds".format(time.time() - start))

timeit.default_timer在Python 2上的POSIX上是time.time因此,除非您使用timeit不正确,否则您应该有一个有效的时间。


resource模块返回的信息包含“实际”时间,但您可以使用它来获取“user”和“sys”次,即“进程在用户模式下花费的CPU总秒数。“”进程在内核模式下花费的CPU总秒数。“相应地:

#!/usr/bin/env python
import os
import time
from subprocess import Popen, STDOUT

DEVNULL = open(os.devnull, 'wb', 0)

start = time.time()
p = Popen(['sleep', '1'], stdout=DEVNULL, stderr=STDOUT)
ru = os.wait4(p.pid, 0)[2]
elapsed = time.time() - start
print(" {:.3f}real {:.3f}user {:.3f}system".format(
       elapsed, ru.ru_utime, ru.ru_stime))

您可以使用psutil.Popen启动子进程,并在子进程运行时获取 便携式计算机中的其他信息(cpu,内存,网络连接,线程,fds,子等)方式。

另见How to get the max memory usage of a program using psutil in Python


为了进行测试(以确保基于time.time()的解决方案产生相同的结果),您可以捕获/usr/bin/time输出:

#!/usr/bin/env python
import os
from collections import deque
from subprocess import Popen, PIPE

DEVNULL = open(os.devnull, 'wb', 0)

time_lines_count = 1 # how many lines /usr/bin/time produces
p = Popen(['/usr/bin/time', '--format=%e seconds'] + 
          ['sleep', '1'], stdout=DEVNULL, stderr=PIPE)
with p.stderr:
    q = deque(iter(p.stderr.readline, b''), maxlen=time_lines_count)
rc = p.wait()
print(b''.join(q).decode().strip())

或者使用-o选项和命名管道:

#!/usr/bin/env python
import os
from contextlib import contextmanager
from shutil     import rmtree
from subprocess import Popen, STDOUT
from tempfile   import mkdtemp

DEVNULL = open(os.devnull, 'wb', 0)

@contextmanager
def named_pipe():
    dirname = mkdtemp()
    try:
        path = os.path.join(dirname, 'named_pipe')
        os.mkfifo(path)
        yield path
    finally:
        rmtree(dirname)

with named_pipe() as path:
    p = Popen(['/usr/bin/time', '--format=%e seconds', '-o', path] + 
              ['sleep', '1'], stdout=DEVNULL, stderr=STDOUT)
    with open(path) as file:
        time_output = file.read().strip()
    rc = p.wait()
print(time_output)

答案 1 :(得分:0)

你的问题与Python时间实用程序的行为不同。在进程写入任何stderr消息之后,时间将写入stderr。您将从shell运行此效果。子进程将完全复制shell命令的行为。

我建议你将stderr重定向到suprocess.PIPE,然后解析它。它不应该太难。

或者,您可以使用-o with time将时间信息写入输出文件。