py-spy 是无侵入式 Python 性能采样工具,Rust 编写、开销极低(<0.1%),无需改代码或重启程序即可定位性能瓶颈。

py-spy 入门与实战

安装

支持 Windows、Mac、Linux:

pip install py-spy  # 推荐
brew install py-spy  # Mac
sudo apt install py-spy  # Linux

验证:py-spy --version,核心命令:toprecorddump


核心命令

top:实时 CPU 占用

实时监控函数 CPU 占用,快速定位最耗时函数。

sudo py-spy top --pid 12345  # 分析运行进程
py-spy top -- python test.py  # 分析脚本

运行后按数字 1-4 排序(1:自身耗时占比;2:总耗时占比;3:自身总时间;4:总时间)。

Linux/Mac 需 sudo(CAP_SYS_PTRACE 权限)。

record:生成火焰图

采样函数调用,生成 SVG 火焰图,直观显示调用关系和耗时。

sudo py-spy record -o profile.svg --pid 12345
py-spy record -o profile.svg -- python test.py

采样后按 Ctrl+C 停止,浏览器打开 profile.svg

火焰图解读

  • Y 轴:调用栈(上层被调用,下层调用者)
  • X 轴:采样时间
  • 横条越长,耗时越多
  • “平顶”函数是主要瓶颈

可选参数

  • --rate 50:采样频率(默认 100Hz)
  • --duration 60:采样时长

dump:查看当前调用栈

即时打印所有线程调用栈,排查程序卡死、无响应问题。

sudo py-spy dump --pid 12345

实战案例

场景:数据加载缓慢

步骤 1:获取 PID

python test_dataset.py &
# 返回 PID,如 116079

测试脚本

import time
import numpy as np
 
class DatasetGenerator:
    def __getitem__(self, item):
        self.do_something()
        return (np.array((item)),)
    
    def do_something(self):
        cnt = 0
        for i in range(100000000):  # 一亿次循环
            cnt += 1
    
    def __len__(self):
        return 50
 
def test_generator():
    data = DatasetGenerator()
    start = time.time()
    for item in range(len(data)):
        data[item]
        print("耗时:", time.time() - start)
        start = time.time()
 
if __name__ == "__main__":
    test_generator()

步骤 2:定位瓶颈

sudo py-spy top --pid 116079
# 发现 do_something 占用 100% CPU

步骤 3:生成火焰图

sudo py-spy record -o dataset_profile.svg --pid 116079

火焰图显示 do_something 横条最长,确认瓶颈。

步骤 4:优化验证

删除一亿次循环后,耗时从 5 秒/条降至 0.001 秒/条。


top 和 record 结果示例

示例代码:

Transclude of learn-py-spy.zip

单进程 CPU 密集型

引入 IO 等待

多进程场景(观察 GIL 的变化):


注意事项

  • 权限:Linux/Mac 需 sudo,Windows 需管理员权限
  • 不支持:PyPy、Jython、嵌入式 Python、C 扩展内部
  • 线上使用:默认 100Hz 开销极低,可用 --rate 50 降频
  • 容器:Docker 加 --cap-add SYS_PTRACE,Kubernetes 配 securityContext

总结

场景命令
本地调试py-spy top/record -- python 脚本
线上排查py-spy top/record --pid 进程号
程序卡死py-spy dump --pid 进程号

py-spy 无侵入、上手快,3 个命令覆盖 80% 性能问题。