<aside>

AI 主导编写,请自行辨别信息

</aside>

1. 问题背景

simulator_v2 是 aiak_ds_tool 库中的推理性能模拟器,给定模型配置和运行参数,通过查询预采集的算子性能数据库,输出逐层/逐算子的耗时预估。

核心调用链:

SimulatorV2App.export_result()
  └─ SimulatorEngine.simulate()          # 对每个 case 逐层模拟
       └─ OperatorRegistry.get_time()    # 对每个算子查询耗时
            └─ DBEngine.query()          # 打开 sqlite3 DB → 执行查询 → 返回行

问题:16-case prefill 场景下 wall time = 32.9s。对一个纯 CPU 数据查询+计算工具而言,显然不可接受。

2. Trace 工具选择:cProfile vs pyinstrument

维度 cProfile pyinstrument
采样方式 确定性(每次调用 hook) 统计采样(~1ms 间隔)
开销 5-30%(侵入式) ≈2%(低干扰)
输出 flat 表:ncalls / tottime / cumtime 调用树 + 按时间折叠
强项 精确调用次数,量化单次开销 快速定位热路径层级关系
弱项 输出扁平,大量 import 噪声 丢失低频调用细节
可视化 snakeviz / speedscope 自带 HTML / speedscope JSON

实战中两者互补:先用 pyinstrument 看树状结构找到热路径,再用 cProfile 量化该路径上的调用次数和单次开销。

命令示例

# cProfile: 输出 pstats 二进制文件
python3 -m cProfile -o /tmp/simv2.pstats -m aiak_infer_tools simulator_v2 \
  --model-config ... --run-config ... --export-output /tmp/out.csv

# pyinstrument: 输出 HTML 调用树
pyinstrument -r html -o /tmp/profile.html \
  -m aiak_infer_tools simulator_v2 ...

# pyinstrument: 输出 speedscope JSON
pyinstrument -r speedscope -o /tmp/profile.json \
  -m aiak_infer_tools simulator_v2 ...

3. 基线测量

测量环境:macOS / Apple Silicon / Python 3.11 / sqlite3 本地文件

场景 Wall Time cProfile 下 DB query 调用次数
decode 1 case 3.52s 2,516
prefill 16 cases 32.9s 35.8s 45,424

<aside>

关键发现:prefill 16 cases 生成 42,960 个算子请求,但 unique DB query key 仅 556 个。

</aside>

77 倍重复查询。大部分时间花在重复打开相同的 sqlite3 文件、执行相同的 SQL、转换相同的行。这是典型的 memoization 机会。

4. 瓶颈诊断

从 pyinstrument baseline 调用树提取的热路径拆解:

调用路径 耗时 占 simulate 比例
Connection.execute(获取表名) 9.0s 33%
sqlite3.connect(打开连接) 1.7s 6%
Connection.close 1.3s 5%
Cursor.execute(实际查询) 2.2s 8%
_resolve_backend_name(listdir) 1.7s 6%
_convert_row + listcomp 2.0s 7%
DBEngine.query 总计 ~22s 81%
dataclasses.asdict(payload 序列化) 4.7s 17%
asdict on query list 2.0s 7%

诊断结论: