Python 带有 /usr/bin/time 的子进程:如何捕获计时信息,但忽略所有其他输出?
Python subprocess with /usr/bin/time: How can I capture timing information, but ignore all other output?
我正在尝试测量通过子进程调用的可执行程序的执行时间(以秒为单位)。我不希望发出可执行文件的输出(stderr 或 stdout)。
我试过timeit和resource库,都没有准确捕捉到进程的时间,貌似只能在Python工作线程中捕捉到时间。
下面的尝试将丢失 stderr 重定向的计时信息 b/c。但是,w/o 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 的输出?
您的问题不在于 Python,而在于 linux 时间实用程序的行为。 time 将在进程写入任何 stderr 消息后写入 stderr。您将从 shell 获得此效果 运行。子进程将完全复制 shell 命令的行为。
我建议您将 stderr 重定向到 suprocess.PIPE 然后解析它。应该不会太难。
或者,您可以使用 -o with time 将您的时间信息写入输出文件。
Elapsed real (wall clock) time used by the process, in seconds.
到 运行 一个带有抑制 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
是 time.time
on POSIX on Python 2 因此你应该得到一个有效时间,除非你对 timeit
的使用不正确。
resource
模块返回的信息 不 包括 "real" 时间,但您可以使用它来获取 "user" 和 "sys" 次,即 "Total number of CPU-seconds that the process spent in user mode." 和 "Total number of CPU-seconds that the process spent in kernel mode." 对应:
#!/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
启动子进程并在子进程 运行ning 时获取 附加信息(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)
我正在尝试测量通过子进程调用的可执行程序的执行时间(以秒为单位)。我不希望发出可执行文件的输出(stderr 或 stdout)。
我试过timeit和resource库,都没有准确捕捉到进程的时间,貌似只能在Python工作线程中捕捉到时间。
下面的尝试将丢失 stderr 重定向的计时信息 b/c。但是,w/o 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 的输出?
您的问题不在于 Python,而在于 linux 时间实用程序的行为。 time 将在进程写入任何 stderr 消息后写入 stderr。您将从 shell 获得此效果 运行。子进程将完全复制 shell 命令的行为。
我建议您将 stderr 重定向到 suprocess.PIPE 然后解析它。应该不会太难。
或者,您可以使用 -o with time 将您的时间信息写入输出文件。
Elapsed real (wall clock) time used by the process, in seconds.
到 运行 一个带有抑制 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
是 time.time
on POSIX on Python 2 因此你应该得到一个有效时间,除非你对 timeit
的使用不正确。
resource
模块返回的信息 不 包括 "real" 时间,但您可以使用它来获取 "user" 和 "sys" 次,即 "Total number of CPU-seconds that the process spent in user mode." 和 "Total number of CPU-seconds that the process spent in kernel mode." 对应:
#!/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
启动子进程并在子进程 运行ning 时获取 附加信息(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)