【问题标题】:How to find execution time of multiple subprocesses?如何查找多个子进程的执行时间?
【发布时间】:2018-11-05 20:25:18
【问题描述】:

我有一个 Linux 命令列表,我正在使用 subprocess 模块运行每个命令,我需要弄清楚如何找到每个命令的执行时间并在列表或字典中返回执行时间,在dict 的情况下,Key 可以是命令名称,值可以是时间,以秒为单位。

(Popen object).poll() == 0 结束的那一刻应该决定结束时间。

我正在使用 Python 3.5.2

例如:

import time
import shlex
from subprocess import *`

#list of commands
commands = ['sleep 5', 'ls -l', 'find /usr','sleep 3','uptime']

#executing each command in the list
for command in commands:
     cmd = shlex.split(command)
     # Need to time this and the moment it is finished executing.
     x = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)

应该返回/打印:

- Commands   Time(secs)
- sleep 5     5.0004   #whatever the time it takes
- ls -l       0.000    #and so on....

【问题讨论】:

  • 你需要命令的输出吗?
  • @Jean-FrançoisFabre 我不知道,所以我不在乎我是否得到输出。

标签: python linux python-3.x time subprocess


【解决方案1】:

Popen 是穷人的“后台运行”技术。你可以控制进程,但如果你想等待它以非轮询方式结束,你不能。

如果您不关心输出,只关心执行时间,您可以将每个 subprocess.call(不再需要 Popen)包装在一个线程中,并更新字典命令 => 花费的时间

启动线程不会阻塞,而是call 阻塞,让您更轻松地计时执行。

import threading,time,subprocess,shlex

time_dict = {}

def time_me(command):
    start_time = time.time()
    cmd = shlex.split(command)
    subprocess.call(cmd)
    time_dict[command] = time.time() - start_time

threads = []
commands = ['sleep 5', 'ls -l', 'find /usr','sleep 3','uptime']

for command in commands:
    t = threading.Thread(target=time_me,args=(command,))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print(time_dict)

【讨论】:

  • 这真的很有效!但是,您能否建议使用 Popen 的解决方案。 Lord Yumma 的解决方案有效,但仅适用于较小的命令,我们可以等到它 poll() 的
  • 使用 Popen & poll 可以避免线程,但必须在活动循环中进行轮询。我不建议这样做。
【解决方案2】:

虽然线程肯定是解决此问题的方法(假设正确处理共享数据),但您也可以运行 while 循环:

import time
import shlex
from subprocess import *

#list of commands
commands = ['sleep 5', 'ls -l', 'sleep 3', 'uptime', 'find /usr']
times = {}
running = {}
#executing each command in the list
for command in commands:
    start_time = time.time()
    cmd = shlex.split(command)
    times[command] = time.time()
    running[command] = Popen(cmd,
                             # no shell=True, shlex already did the split
                             stdout=PIPE, stderr=PIPE)

while len(running):
    finished = set()
    for cmd, proc in running.items():
        if proc.poll() is not None:
            times[cmd] = time.time() - times[cmd]
            finished.add(cmd)
        else:
            proc.communicate() # drain the pipe

    for cmd in finished:
        del running[cmd]

print(times)

请注意,这不会阻塞(与其线程替代相反),因此它最终可能会占用您的 CPU。为了减轻负载,您可以在循环结束时添加对time.sleep 的调用,这会使结果稍微不准确。

编辑: 由于您在示例中使用了管道,因此我假设您希望使用(某些)命令的输出进行一些有意义的处理。不管你愿不愿意,如果你不做proc.communicate,你最终会填满你的管道,从而阻塞这个过程。或者,您当然可以将输出重定向(到某个文件或 /dev/null)。与进程通信也可能会改变结果,处理也是如此:)

【讨论】:

  • 感谢您的回答。但是,在添加了诸如“find /”和“find /usr”之类的额外命令后,已经过去了超过 15 分钟,但仍在等待它执行。
  • 仍然没有响应,而且现在它使用了高达 40% 的 CPU,尽管我使用的是 PC 64 位 Core i5,2.3 ghz(4 个 CPU)
  • 我不确定繁忙循环如何影响“查找”,但我已经明确提到了 CPU 问题。尝试 time.sleep(0.1) 作为 while 循环的最后一条语句。
  • 我已经尝试过 time.sleep,它不会影响持续时间,这是另外一回事,我在另一个函数中对“find”命令进行了计时,但耗时不超过 10 秒。
  • 啊,看来您的管道已满。我已经更新了我的回复以考虑到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 2016-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多