性能2-你的瓶颈在哪里?CPU时间 vs 挂钟时间

目录

你的瓶颈在哪里?CPU时间 vs 挂钟时间

如果你的进程运行缓慢,可能是因为它非常占用CPU——或者可能是因为它在等待I/O(网络或文件系统)、锁,或者只是在休眠。但如何判断呢?

有多种方法可以回答这个问题,但在本文中,我将介绍可能是最简单的启发式方法:比较CPU时间和挂钟时间。

我将首先展示如何在整个进程运行期间测量它,然后演示如何使用Python随时间测量它。


挂钟时间 vs CPU时间

挂钟时间测量的是实际经过的时间,就像你看着墙上的时钟一样。CPU时间是CPU忙碌的秒数。

为了理解性能,你需要比较这两者。例如,host命令会向DNS服务器发送查询,以找出我的域名使用的邮件服务器:

$ host -t MX pythonspeed.com
pythonspeed.com mail is handled by in2-smtp.messagingengine.com.
pythonspeed.com mail is handled by in1-smtp.messagingengine.com.

这个命令在哪里花费了时间?

我们可以使用Unix系统上的time命令来大致了解:

$ time host -t MX pythonspeed.com
pythonspeed.com mail is handled by in1-smtp.messagingengine.com.
pythonspeed.com mail is handled by in2-smtp.messagingengine.com.

real    0m0.069s
user    0m0.006s
sys     0m0.005s

time命令运行传递给它的参数,并记录三行额外的输出:

  • real:挂钟时间。
  • user:进程的CPU时间。
  • sys:由于进程的系统调用而导致的操作系统CPU时间。

在这个例子中,挂钟时间比CPU时间要长,这表明进程花费了大量时间等待(大约58毫秒),而不是一直在进行计算。它在等待什么?可能是在等待我的ISP的DNS服务器的网络响应。

换句话说,host命令的瓶颈不是CPU,而是网络。 即使我购买了一台CPU速度翻倍的计算机,对返回结果的时间影响也非常小。

重要提示: 如果机器上运行了许多其他进程,这些进程会占用一些CPU。如果它们非常繁忙,你正在基准测试的进程可能最终会等待CPU空闲。

目前只需记住,如果你在测量性能,应该尽量在一台没有其他进程运行的安静机器上进行。如果你正在进行严格的基准测试,可能会对在噪声中甚至不同CPU模型下进行可靠基准测试的技术感兴趣。


从不同的比率中可以学到什么

在上面的例子中,CPU时间比挂钟时间要短,但也可能存在其他关系。将可能的关系表示为(CPU时间) / (挂钟时间)的比率更容易理解,即CPU/秒

如果这是一个单线程进程:

  1. CPU/秒 ≈ 1 进程花费了所有时间使用CPU。更快的CPU可能会使程序运行得更快。
  2. CPU/秒 < 1 数值越低,进程花费在等待(网络、硬盘、锁、其他进程释放CPU,或者只是休眠)的时间越多。例如,如果CPU/秒是0.75,那么25%的时间花费在等待上。

如果这是一个多线程进程,并且你的计算机有N个CPU和至少N个线程,CPU/秒可以高达N

  1. CPU/秒 < 1 进程花费了大量时间等待。
  2. CPU/秒 ≈ N 进程饱和了所有的CPU。
  3. 其他值: 进程使用了等待和CPU的某种组合,仅凭此测量结果很难判断瓶颈是什么。你可以通过线程数量来获得一些线索——如果你运行了2个线程并且CPU/秒 = 2,你知道你已经饱和了。

使用psutil Python库随时间测量CPU使用情况

到目前为止,我们一直在查看整个进程运行期间的CPU使用情况,但实际上我们可以随时间测量它。在Python中,使用psutil库可以轻松实现这一点,它可以让你获取有关进程的许多有用信息。

你可以查看系统时间、用户时间,以及子进程的相应时间:

>>> import psutil
>>> p = psutil.Process(pid=6045)
>>> p.cpu_times()
pcputimes(user=345.45, system=12.8, children_user=220.03, children_system=17.37)

为了查看随时间的变化,我们可以编写一个小工具,每秒打印出任意进程及其子进程的CPU/秒

import sys
import time
import psutil

process = psutil.Process(int(sys.argv[1]))
last_times = process.cpu_times()

while process.is_running():
    time.sleep(1)
    times = process.cpu_times()
    usage = sum(times) - sum(last_times)
    last_times = times
    print("{:.2f} CPU/sec".format(usage))

我们可以使用它来查看PID为6045的进程使用了多少CPU:

$ python cpu-over-time.py 6045
0.03 CPU/sec
0.09 CPU/sec
0.12 CPU/sec
0.40 CPU/sec
0.50 CPU/sec
0.13 CPU/sec

最窄的瓶颈是需要修复的

如果90%的进程时间花费在等待网络上,那么专注于CPU处理优化通常没有意义。因此,性能优化的第一步始终是识别瓶颈。

根据你构建的内容,瓶颈通常不是CPU。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李星星BruceL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值