【问题标题】:Python subprocess with /usr/bin/time: How can I capture timing information, but ignore all other output?/usr/bin/time 的 Python 子进程:如何捕获时间信息,但忽略所有其他输出?
【发布时间】:2015-04-15 17:28:22
【问题描述】:

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

我试过timeit和资源库,但都没有准确捕捉到进程的时间,貌似只捕捉到Python工作线程中的时间。

下面的这种尝试将丢失 stderr 重定向的时间信息 b/c。但是,如果没有标准错误重定向,将发出命令“f_cmd”标准错误输出。

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 的输出?

【问题讨论】:

    标签: python python-2.7 time stdout stderr


    【解决方案1】:

    %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 模块返回的信息包含“真实”时间,但您可以使用它来获取“用户”和“系统”时间,即,“总数进程在用户模式下花费的 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)
    

    【讨论】:

      【解决方案2】:

      您的问题不在于 Python,而在于 linux 时间实用程序的行为。 time 将在进程写入任何 stderr 消息后写入 stderr。从 shell 运行它会得到这种效果。 Subprocess 将完全复制 shell 命令的行为。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-26
        • 1970-01-01
        • 1970-01-01
        • 2013-10-14
        • 1970-01-01
        • 2016-07-21
        • 2013-06-02
        • 2018-05-20
        相关资源
        最近更新 更多