7

很简单,我想从 Python 脚本中运行一个外部命令/程序,一旦完成,我还想知道它消耗了多少 CPU 时间。

硬模式:并行运行多个命令不会导致 CPU 消耗结果不准确。

4

5 回答 5

17

在 UNIX 上: (a) 使用资源模块(另见 icktoofay 的回答),或 (b) 使用time命令并解析结果,或 (c) 使用/proc文件系统,解析 /proc/[pid]/stat 和解析出utimestime字段。最后一个是特定于 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 调度程序获取数据。

于 2012-12-18T13:11:38.203 回答
4

在可用的平台上,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将包含执行所用的时间lswork_time并将包含执行所用的时间python -c "print (2 ** 512) ** 200"

于 2012-12-15T05:43:29.250 回答
3

您可以在 Python 中进行计时,但如果您想知道程序的总体 CPU 消耗情况,这样做有点愚蠢。最好的办法是只使用 GNUtime程序。它甚至成为大多数操作系统的标准配置。

于 2012-12-15T03:42:23.820 回答
2

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()您应该取最小值,而不是尝试计算平均值或标准偏差。

于 2012-12-17T05:23:08.497 回答
1

您可以使用 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
于 2012-12-15T03:59:29.367 回答