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 | 阻塞时间 | 读数 |
|---|---|---|
| None | 0 毫秒 | 0.0(首次) |
| 0.1 | 100.2 毫秒 | 7.1% |
| 0.5 | 501.1 毫秒 | 7.8% |
| 1.0 | 1000.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 | 耗时 |
|---|---|
| 只要 pid | 9 毫秒 |
| 带 name | 875 毫秒 |
| 带 memory_info | 856 毫秒 |
| name + memory_info | 832 毫秒 |
| 再加 cpu_percent | 930 毫秒 |
只取 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 上的同一批调用耗时 / 传感器接口(温度、风扇)的可用性 / 长时间采样的内存增长。








