一、和 perf 的区别#
perf(采样):
每隔 N 微秒采样一次 CPU 的 PC 寄存器
快(几乎不影响程序运行速度)
统计结果有误差(采样丢失)
Callgrind(模拟):
用 Valgrind 的虚拟 CPU 模拟执行每条指令
慢(慢 20-30 倍)
结果精确(统计了每一条指令、每次缓存访问)
二、基本用法#
# 运行 Callgrind
valgrind --tool=callgrind ./my_program
# 输出文件:callgrind.out.12345 (pid=12345)
# 查看函数排行
callgrind_annotate callgrind.out.12345
# 输出示例:
# --------------------------------------------------------------------------
# Ir (指令数) Dr (读) Dw (写) L1m (L1 Miss) L2m (L2 Miss) function
# --------------------------------------------------------------------------
# 12,345,678 4,321,000 2,100,000 123,456 12,345 motion_search
# 8,234,567 3,100,000 1,500,000 89,012 8,901 dct_transform
# 2,345,678 900,000 400,000 234,567 23,456 memcpy
# 567,890 200,000 100,000 1,234 123 frame_copy
# 重要指标:
# Ir(Instructions)—— 总指令数,越多的函数越值得优化
# L1m / L2m —— 缓存不命中率,越高说明内存访问模式越差
# 可以清晰看到:
# motion_search 指令数最多(12M),且 L1 Miss 也高(123K)
# → 优先优化这个函数,且优化方向是改善内存访问模式
三、可视化分析#
# 用 KCachegrind(GUI 工具)打开
kcachegrind callgrind.out.12345
# 可以在图形界面中看到:
# - 函数调用关系的图(谁调了谁,花费了多少指令)
# - 每个函数的指令数/缓存 Miss 数/分支预测失败数
# - 源码级别的指令开销
# - 热路径(哪个调用链最耗时)