gprof 必须用 -pg 且禁用优化编译,推荐 g++ -g -O0 -pg;需链接时也加 -pg;运行后生成 gmon.out 才能分析,程序须正常退出;对短时、I/O 密集、多线程程序无效。

编译必须加 -pg 且禁用优化
gprof 依赖插桩(instrumentation),不是采样,所以编译阶段就得埋点。只加 -pg 不够——如果同时开了 -O2,GCC 会内联函数、重排逻辑,导致插桩位置错位、调用关系断裂,main 可能显示 0.0% 耗时,或者热点函数直接消失。
正确做法是:g++ -g -O0 -pg main.cpp -o app。其中 -g 保证函数名可读,-O0 防止内联干扰,-pg 插入计数逻辑。若想兼顾一定性能又保留基本调用结构,可用 -O1 -pg,但需验证报告是否仍可信。
- 别用
-O2 -pg:模板实例、std::vector::push_back等高频小函数大概率被吞掉 - 链接命令也得带
-pg:比如g++ -pg main.o utils.o -o app,不能只在编译单个 .cpp 时加 - 确保
gmon.out是本次运行生成的:旧文件不会自动覆盖,手动删掉再跑
运行后必须生成 gmon.out 才能分析
程序退出时才会写 gmon.out,所以不能靠 Ctrl+C 中断,也不能让程序 crash —— 否则文件为空或损坏。尤其注意短时程序(比如运行 gmon.out 常常数据稀疏甚至为空,gprof 报告不可信。
验证是否成功生成:ls -l gmon.out 应该有几百字节以上;再试 file gmon.out,输出应含 “GNU gmon” 字样。如果没生成,检查程序是否真执行完了、当前目录是否正确、有没有权限写文件。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 多线程程序只记录主线程:其他线程的函数调用不会出现在报告里
- I/O 等待时间不计入函数耗时:比如
read()或sleep()的时间常被归为“未归因”,看起来像 CPU 空转 - 共享库(.so)默认不被跟踪:除非你用
-pg编译并链接了所有依赖库
看 report 要盯 % time 和 calls 两个字段
gprof 输出分 Flat profile 和 Call graph。优先看 Flat profile 里的三列:% time(函数自身耗时占比)、calls(调用次数)、self ms/call(单次开销)。真正的问题往往藏在组合里,不是单看最大百分比。
例如:vec_push_back 占 22%,calls = 500000,self ms/call = 0.044 → 这说明它单次很轻,但总量吃资源,得查是不是在循环里反复扩容;而 heavy_calc 占 68%,calls = 1,self ms/call = 1200 → 直接进函数看算法瓶颈。
- 用
gprof app gmon.out | head -n 50快速扫 top 函数 - 过滤自己代码:
gprof app gmon.out -q "mylib::|main",避免被std::或 libc 冲散注意力 -
self ms/call异常高?可能是锁竞争、异常路径、或内存局部性差(比如随机访问大数组)
gprof 失效时该换什么工具
当遇到以下情况,gprof 就不是最佳选择:程序运行太快、大量系统调用、多线程、模板/内联函数占主导。这时候 perf 更可靠,因为它基于硬件计数器采样,不依赖插桩。
最简切换路径:g++ -g -O2 main.cpp -o app(去掉 -pg,保留调试信息),然后 perf record -g -F 99 ./app,最后 perf report -n --no-children。你会看到真实调用栈,包括 std::string::c_str() 在哪被高频调用、malloc 是否卡在锁上。
- 火焰图比文本更直观:用
perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flame.svg - gprof 对短时、I/O 密集、多线程程序基本无效,不是配置问题,是原理限制
- Valgrind 的 Callgrind 模块可补 gprof 空缺,但速度慢 10–30 倍,适合小规模复现


















