psutil 是跨平台的系统信息库,一个 import 就能拿到 CPU、内存、磁盘、网络、进程。监控面板、资源看板、自动化脚本里的「看机器状态」大多用它。

它的接口看着都像一行调用,开销却差三个数量级。这篇把每个常用调用单独计时了一遍,顺带记下几个容易误判的默认行为。

cpu_percent 的第一次调用永远是 0

>>> psutil.cpu_percent(interval=None)
0.0              # 第一次恒为 0
>>> psutil.cpu_percent(interval=None)
7.1              # 第二次才有值

它算的是「两次调用之间的差值」,第一次没有上一次采样做基准,只能返回 0。写监控脚本时如果只取一次值,拿到的永远是 0,看起来像 CPU 完全空闲。

interval 参数决定这次要不要阻塞采样:

interval阻塞时间读数
None0 毫秒0.0(首次)
0.1100.2 毫秒7.1%
0.5501.1 毫秒7.8%
1.01000.4 毫秒9.1%

传了数字就真的等那么久。写进 Web 请求的 handler 里,一个 1 秒的采样会把接口拖到 1 秒以上。

单次调用的耗时差三个数量级

每个调用跑 2000 次取中位,单位是微秒:

调用单次耗时
Process.memory_info()2.5 微秒
cpu_percent(None)15.5 微秒
cpu_times()17.0 微秒
disk_usage("C:")25.5 微秒
net_io_counters()1748.6 微秒
virtual_memory()5860 微秒

最慢的 virtual_memory() 单次要 5.9 毫秒。同样的数据自己调 Windows API 只要 0.9 微秒,差 6658 倍:

psutil.virtual_memory()              5860 微秒/次
GlobalMemoryStatusEx(ctypes 直调)     0.9 微秒/次

关掉 gc 再测,数字不变(5953 微秒对 6020 微秒),所以不是垃圾回收的锅。要在高频循环里读内存,这个差距会直接体现出来:一千次就是 5.9 秒。

遍历进程:加一个属性多花 800 毫秒

机器上共 355 个进程,只改 attrs 参数,耗时完全不同:

attrs耗时
只要 pid9 毫秒
带 name875 毫秒
带 memory_info856 毫秒
name + memory_info832 毫秒
再加 cpu_percent930 毫秒

只取 pid 是 9 毫秒,加任何一个需要打开进程句柄的属性就跳到 800 毫秒以上,差 97 倍。原因是取进程名要逐个 OpenProcess,这一步在 Windows 上很贵。

所以做进程列表时先想清楚要几个字段。只要 pid 和名字的场景,跟要内存、CPU、命令行一起取的场景,成本不在一个量级。

内存和交换区的读数

total      31.9 GB
used       23.4 GB
available   8.5 GB
percent    73.4%
swap total  6.8 GB / used 2.1 GB

percent 是按 used 算的,和任务管理器显示的百分比可能对不上,因为两者对「已用」的统计口径不同。做告警阈值时以 percent 为准,别自己拿 used/total 再算一遍。

进程消失时会抛异常

>>> psutil.Process(999999)
psutil.NoSuchProcess: process PID not found (pid=999999)

遍历过程中进程退出是常态,process_iter 内部会吞掉这类异常,但你自己拿着 pid 去构造 Process 时不会。批量采集时要在外面包一层 NoSuchProcess 和 AccessDenied,否则一个进程消失就让整轮采集中断。

本次 355 个进程全部能读到 memory_info,0 个 AccessDenied,说明当前权限足够。换成普通用户跑服务时,系统进程会开始报权限错误。

什么时候别用它

每秒都要读一次内存的循环。5.9 毫秒乘上一万次是 59 秒,这种场景直接调系统 API 或者降低采样频率。

只想知道自己进程占多少内存。Process.memory_info() 只要 2.5 微秒,比 virtual_memory() 快两千倍,能拿到单进程就别读整机。

要精确到每次系统调用的场景。它是封装层,值本身来自系统,测试里 mock 掉 psutil 的返回值比 mock 系统调用简单,这点反而是它方便的地方。

没测的部分列一下,3 项:Linux 与 macOS 上的同一批调用耗时 / 传感器接口(温度、风扇)的可用性 / 长时间采样的内存增长。