gprof适合单线程简单项目快速定位函数耗时,需编译加-pg -g,运行生成gmon.out后用gprof分析;perf更轻量精准,支持多线程和硬件事件;VS Profiler适合Windows;callgrind深挖内存与I/O问题。

用 gprof 快速定位函数级耗时(Linux + GCC)
它不依赖额外库,编译时加 -pg 就能跑,适合快速摸清「哪个函数吃时间」。但注意:它靠插桩统计,会拖慢程序(尤其高频调用函数),且不支持多线程调用栈合并。
- 编译命令必须同时带
-pg和-g:g++ -pg -g main.cpp -o main - 运行一次生成
gmon.out:./main(别忘了这步,否则没数据) - 分析命令:
gprof ./main gmon.out | less,重点关注% time和calls列 - 如果程序中途崩溃或提前退出,
gmon.out可能写不全,结果为空或失真
perf 抓取真实 CPU 级热点(推荐 Linux 生产环境)
perf 是内核级采样,开销极小,支持多线程、系统调用、硬件事件(如 cache-misses),比 gprof 更准也更“轻”。但它默认不记录 C++ 符号名,需确保编译带 -g 且未 strip。
- 基础采样:
perf record -g ./main(-g开启调用图) - 查看火焰图式汇总:
perf report -g --no-children,按↑/↓排序看 top 函数 - 常见坑:Ubuntu 默认禁用
perf_event_paranoid,报Permission denied就执行sudo sysctl -w kernel.perf_event_paranoid=0 - 如果看到一堆
[unknown],说明调试信息缺失,重编译加-g -O2(别用-O3,可能内联过度导致符号丢失)
Windows 下用 Visual Studio Profiler 而不是手写 clock()
自己用 std::chrono::high_resolution_clock 包裹某段代码测耗时,只能验证「你猜得对不对」,没法发现隐藏瓶颈(比如内存分配、锁竞争、STL 内部迭代)。VS 自带的 CPU 使用率工具能直接关联源码行号,还带堆分配分析。
- 启动方式:菜单栏
Debug → Performance Profiler,勾选CPU Usage - 务必用
Release配置运行(Debug下优化关闭,结果无参考价值) - 避免在
main()开头就打点——VS 启动 Profiler 有毫秒级延迟,短于 10ms 的逻辑容易被漏掉 - 如果项目含动态库,确保 PDB 文件和二进制在同目录,否则无法映射到源码
别忽略 I/O 和内存分配——valgrind --tool=callgrind 的真实用途
很多 C++ 程序卡在 std::vector::push_back 扩容、std::string 频繁构造、或 fread 等待磁盘,这些在 gprof 或 perf 里只显示为「系统调用时间」,看不出根因。callgrind 能把每次 malloc/new、文件读写都记下来,代价是慢 10–50 倍。
立即学习“C++免费学习笔记(深入)”;
- 运行命令:
valgrind --tool=callgrind --dump-instr=yes --collect-jumps=yes ./main - 结果默认输出
callgrind.out.PID,用kcachegrind callgrind.out.PID图形化查看(Ubuntu 直接apt install kcachegrind) - 重点看右侧面板的
Ir (Instruction Read)和Dr/Dw (Data Read/Write),数值异常高往往意味着缓存不友好或冗余拷贝 - 如果程序本身做大量浮点计算,
callgrind会严重夸大耗时——它模拟指令执行,不是真实时间,仅作相对比较
perf 或 VS Profiler 锁定模块,再用 callgrind 深挖内存行为;而 gprof 只适合单线程、编译简单的老项目。符号没对上、采样没生效、结果为空——八成是编译选项或权限问题,不是工具不行。









