Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 be emitted.

I have tried the timeit and resource libraries, but neither accurately captures the time of the process, seemingly it only captures the timing in the Python worker thread.

This attempt below will lose the timing info b/c of the stderr redirect. However, w/o the stderr redirect, the command 'f_cmd' stderr output will be emitted.

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

How do I ignore all output of f_cmd but retain the output of /usr/bin/time?

like image 253
rkemp Avatar asked Oct 28 '25 09:10

rkemp


1 Answers

%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)
like image 84
jfs Avatar answered Oct 31 '25 07:10

jfs



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!