【问题标题】:Time an external program whose output is being processed by Python为 Python 处理其输出的外部程序计时
【发布时间】:2013-04-03 12:45:58
【问题描述】:

我想测量我的 Python 脚本使用其输出的外部程序的执行时间。

调用extprogram产生输出的程序,此刻我做了类似的事情:

import time
import subprocess

def process_output(line):
   ...
   ...
   return processed_data

all_processed_data = []

ts = time.time()
p = subprocess.Popen("extprogram", stdout=subprocess.PIPE)

for line in p.stdout:
    all_processed_data.append(process_output(line))
te = time.time()
elapsed_time = te - ts

这不能按预期工作,因为我测量的是extprogram 的执行时间加上处理其输出所需的时间。

extprogram 会产生大量数据,因此我想像现在这样使用循环将其输出“流式传输”到我的 Python 程序中。 当extprogram 终止而不是等待处理所有输出时,如何评估te

【问题讨论】:

  • 如果你使用的是 linux 有一个time 命令。
  • 你会如何在我的程序中使用它?
  • 嗯,时间的正常使用是time extprogram args,时间输出到stderr。我猜你可以做类似p = subprocess.Popen(["time","extprogram"],stdout = subprocess.PIPE,stderr=subprocess.PIPE)

标签: python external timing


【解决方案1】:

由于你是在 Unix 下,你可以使用time 命令。原理如下:

import sys
import subprocess

p = subprocess.Popen(["time", "ls"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

for line in p.stdout:  # ls output
    sys.stdout.write(line)

time_output = p.stderr.readlines()

print "Stderr:", ''.join(time_output)

在我的机器上,这给出了:

Stderr:         0.01 real         0.00 user         0.00 sys

总处理器时间是user + sys 时间(real 是挂钟时间,它通常不代表程序使用了多少处理器时间:例如,sleep 5,@ 987654328@时间为5秒,而usersys时间为0)。

之所以有效,是因为time 输出了对实际执行时间的详细统计(不仅仅是挂墙时间,这取决于正在运行的其他进程等),并且对标准错误输出也是如此。您可以解析标准错误并获取时序信息。

如果您将数据输出到可能干扰time 命令解析的标准错误,则此方法可能不实用。

另外,我还没有检查上述代码不会发生死锁(我不确定如果调用的程序向标准错误打印很多内容会发生什么:程序是否会阻塞,直到标准错误缓冲区读取,如果 Python 程序正在读取标准输出,这可能不会发生?)。也就是说,如果你知道定时程序没有或很少有关于标准错误的数据,我相信上面的代码不会死锁。

【讨论】:

  • 我看到了两个并发症。 1. 实际上extprogram 已经将其他外部应用程序的输出作为输入。 2.times的输出被重定向到stdout,干扰了extprogram的输出
  • 第一点并不是真正的问题:您只需通过管道传送到time extprogram 而不是extprogram,结果是一样的!至于你的第二点,实际上time的输出是not重定向到标准输出的,所以不会有干扰(你可以在代码中看到这个:time_output = p.stderr.readlines()—not p.stdout).
  • 谢谢,这个解决方案可能适用于这种情况。但是一般来说就没有别的办法了吗?我的意思是如果我想在 Windows 中使用该程序怎么办?也许使用多线程我可以检查 p.poll() 并仅在完成后运行时间函数?
  • 我添加了一个应该跨平台工作的多线程答案。虽然它仍然是挂钟,但比我最初的答案灵活得多。
  • @LucaCerone:关于 Windows:使用标准 Python 模块(我将 cProfile 放在一边)测量执行时间只有一种简单的方法:通过 timeit 模块。但是,该模块报告了在 Python 函数中花费的总时间;问题是这个时间不一定是处理器时间(例如,对于sleep 5 命令,timeit 报告执行时间为 5 秒)。
【解决方案2】:

以下仍然使用“挂钟”时间,但可以替代使用主机系统时间命令。执行和计时被分成不同的线程,并且可以在执行任何处理之前停止计时器。

from multiprocessing import Event
import threading
import time
import subprocess

def timing(event):
    print "timer starts"
    ts = time.time()
    event.wait()
    te = time.time()
    elapsed_time = te - ts
    print "Elapsed Time " + str(elapsed_time)

def execution(event): 
    for i in range(0,1000):
        p = subprocess.Popen("ls", stdout=subprocess.PIPE)
    event.set()

if __name__ == '__main__':  
    event = Event()
    e = threading.Thread(target=execution, args=(event,))
    t = threading.Thread(target=timing, args=(event,))
    t.start()  
    e.start() 
    while not event.is_set():
        print "running..."
        time.sleep(1)

这给了我以下输出:

timer starts
running...
running...
Elapsed Time 1.66236400604

或者您可以将接收“extprogram”的输出与输出处理分开。

例如:

ts = time.time()
p = subprocess.Popen("extprogram", stdout=subprocess.PIPE)

for line in p.stdout:
    tempdata.append(line)

te = time.time()
elapsed_time = te - ts

for line in tempdata:
    all_processed_data.append(process_output(line))

【讨论】:

  • 谢谢@StuGrey。其实我做不到。正如我所说,外部程序会生成大量数据,并且无法将其所有内容存储在内存中。即使我可以,您建议的方式也会测量外部程序的执行时间加上将其内容存储在列表中的时间(好吧,它可能很快,但我想要一个公正的测量)。
  • 测量执行时间一般不能用“挂钟”时间来完成:其他运行的进程可以使测量的时间比实际运行时间长得多。
  • @EOL 谢谢,但目前挂钟很好。不过你是对的,在 python 中最好有一个函数以类似于 linux 中的 time 命令的方式报告 cpu 时间
猜你喜欢
  • 1970-01-01
  • 2011-11-28
  • 2016-07-31
  • 2020-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-17
  • 2010-10-06
相关资源
最近更新 更多