很简单,我想从 Python 脚本中运行一个外部命令/程序,一旦完成,我还想知道它消耗了多少 CPU 时间。
硬模式:并行运行多个命令不会导致 CPU 消耗结果不准确。
在 UNIX 上: (a) 使用资源模块(另见 icktoofay 的回答),或 (b) 使用time命令并解析结果,或 (c) 使用/proc文件系统,解析 /proc/[pid]/stat 和解析出utime
和stime
字段。最后一个是特定于 Linux 的。
使用示例resource
:
import subprocess, resource
usage_start = resource.getrusage(resource.RUSAGE_CHILDREN)
subprocess.call(["yourcommand"])
usage_end = resource.getrusage(resource.RUSAGE_CHILDREN)
cpu_time = usage_end.ru_utime - usage_start.ru_utime
注意:fork/execvp 不是必须的,subprocess.call()
否则其他子进程方法在这里都可以,而且更容易使用。
注意:您可以使用 subprocess.Popen 或 subprocess.call 和线程同时从同一个 python 脚本运行多个命令,但是资源不会返回它们正确的单个 cpu 时间,它会返回它们在调用 getrusage 之间的时间总和; 要获得单独的时间,请为每个命令运行一个小 python 包装器以按上述方式计时(可以从您的主脚本启动那些),或使用time
下面的方法,该方法将与多个同时命令正常工作(时间基本上就是这样一个包装器) .
使用示例time
:
import subprocess, StringIO
time_output = StringIO.StringIO()
subprocess.call(["time", "yourcommand", "youroptions"], stdout=time_output)
# parse time_output
在 Windows 上:您需要以某种方式使用性能计数器(又名“性能数据助手”)。这是底层 API 的C 示例。要从 python 中获取它,您可以使用以下两个模块之一:win32pdh(pywin32 的一部分;示例代码)或pyrfcon(跨平台,也适用于 Unix;示例代码)。
这些方法中的任何一种实际上都满足上述“硬模式”要求:即使在繁忙的系统上运行不同进程的多个实例,它们也应该是准确的。在这种情况下,与在空闲系统上仅运行一个进程相比,它们可能不会产生完全相同的结果,因为进程切换确实有一些开销,但它们会非常接近,因为它们最终从 OS 调度程序获取数据。
在可用的平台上,该resource
模块可能会提供您需要的内容。如果您需要同时为多个命令计时,您可能希望(对于要运行的每个命令)fork,然后创建子进程,以便仅获取该进程的信息。这是您可以执行此操作的一种方法:
def start_running(command):
time_read_pipe, time_write_pipe = os.pipe()
want_read_pipe, want_write_pipe = os.pipe()
runner_pid = os.fork()
if runner_pid != 0:
os.close(time_write_pipe)
os.close(want_read_pipe)
def finish_running():
os.write(want_write_pipe, 'x')
os.close(want_write_pipe)
time = os.read(time_read_pipe, struct.calcsize('f'))
os.close(time_read_pipe)
time = struct.unpack('f', time)[0]
return time
return finish_running
os.close(time_read_pipe)
os.close(want_write_pipe)
sub_pid = os.fork()
if sub_pid == 0:
os.close(time_write_pipe)
os.close(want_read_pipe)
os.execvp(command[0], command)
os.wait()
usage = resource.getrusage(resource.RUSAGE_CHILDREN)
os.read(want_read_pipe, 1)
os.write(time_write_pipe, struct.pack('f', usage.ru_utime))
sys.exit(0)
然后,您可以使用它来运行一些命令:
get_ls_time = start_running(['ls'])
get_work_time = start_running(['python', '-c', 'print (2 ** 512) ** 200'])
执行该代码后,这两个命令应该并行运行。当您想等待它们完成并获取它们执行的时间时,请调用返回的函数start_running
:
ls_time = get_ls_time()
work_time = get_work_time()
现在ls_time
将包含执行所用的时间ls
,work_time
并将包含执行所用的时间python -c "print (2 ** 512) ** 200"
。
您可以在 Python 中进行计时,但如果您想知道程序的总体 CPU 消耗情况,这样做有点愚蠢。最好的办法是只使用 GNUtime
程序。它甚至成为大多数操作系统的标准配置。
python的timeit模块对于基准测试/分析目的非常有用。除此之外,您甚至可以从命令行界面调用它。要对外部命令进行基准测试,您可以这样:
>>> import timeit
>>> timeit.timeit("call(['ls','-l'])",setup="from subprocess import call",number=1) #number defaults to 1 million
total 16
-rw-rw-r-- 1 nilanjan nilanjan 3675 Dec 17 08:23 icon.png
-rw-rw-r-- 1 nilanjan nilanjan 279 Dec 17 08:24 manifest.json
-rw-rw-r-- 1 nilanjan nilanjan 476 Dec 17 08:25 popup.html
-rw-rw-r-- 1 nilanjan nilanjan 1218 Dec 17 08:25 popup.js
0.02114391326904297
最后一行是返回的执行时间。这里,第一个参数timeit.timeit()
是调用外部方法的代码,setup
参数指定要在时间测量开始之前运行的代码。number
参数是您希望运行指定代码的次数,然后您可以将返回的时间除以number
得到平均时间。
您还可以使用该timeit.repeat()
方法,该方法采用类似的参数,timeit.timeit()
但采用一个附加repeat
参数来指定timeit.timeit()
应调用的次数,并返回每次运行的执行时间列表。
注意:该timeit.timeit()
方法返回的执行时间是挂钟时间,而不是 CPU 时间。因此,其他进程可能会干扰计时。因此,如果timeit.repeat()
您应该取最小值,而不是尝试计算平均值或标准偏差。
您可以使用 ipython 的%time
魔法功能来做到这一点:
In [1]: time 2**128
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
Wall time: 0.00
Out[1]: 340282366920938463463374607431768211456L
In [2]: n = 1000000
In [3]: time sum(range(n))
CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s
Wall time: 1.37
Out[3]: 499999500000L