Python subprocess with /usr/bin/time: How can I capture timing information, but ignore all other output?

后端 未结 2 1242
情深已故
情深已故 2020-12-10 09:38

I am trying to measure the execution time in seconds of an executable program invoked via subprocess. I do not want the output of the executable (either stderr or stdout) to

相关标签:
2条回答
  • 2020-12-10 09:51

    Your problem isn't with Python so much as it is with the behavior of the linux time utility. time will write to stderr after any stderr messages the process writes. You would get this effect running it from the shell. Subprocess is going to exactly copy the behavior of the shell command.

    I would suggest you redirect stderr to suprocess.PIPE and then parse it. It shouldn't be too hard.

    Alternatively, you could use -o with time to write your time info to an output file.

    0 讨论(0)
  • 2020-12-10 09:58

    %e /usr/bin/time format is:

    Elapsed real (wall clock) time used by the process, in seconds.

    To run a subprocess with suppressed stdout/stderr and get the elapsed time:

    #!/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 is time.time on POSIX on Python 2 therefore you should have got a valid time unless your usage of timeit is incorrect.


    The information returned by resource module does not include the "real" time, but you could use it to get "user" and "sys" times i.e., "Total number of CPU-seconds that the process spent in user mode." and "Total number of CPU-seconds that the process spent in kernel mode." correspondingly:

    #!/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))
    

    You could start a subprocess using psutil.Popen and get while the child process is running additional info (cpu, memory, network connections, threads, fds, children, etc) in a portable way.

    See also, How to get the max memory usage of a program using psutil in Python.


    For testing (to make sure that time.time()-based solution produces the same results), you could capture /usr/bin/time output:

    #!/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())
    

    Or using -o option with a named pipe:

    #!/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)
    
    0 讨论(0)
提交回复
热议问题