gprof 不能直接分析 release 版本程序,必须使用 -pg 编译且避免过度优化,否则函数内联或删除会导致调用链断裂、gmon.out 空或仅含 __libc_start_main。

gprof 能不能直接分析 Release 版本的程序
不能,除非你加 -pg 编译且不优化过度。gprof 依赖编译器在函数入口/出口插入计数桩(profiling hooks),而 -O2 或更高优化等级可能内联函数、删除看似“无用”的调用,导致 main 调用链断裂、gmon.out 中只有扁平统计甚至全为空。
实操建议:
- 用
g++ -O2 -pg -g编译,-g不影响性能但保留符号,方便gprof显示函数名 - 避免
-fomit-frame-pointer(某些旧 GCC 默认开),它会让调用图(call graph)失效 - 确认运行后生成了
gmon.out:执行完程序再检查当前目录,没生成=没触发 profiling 桩
为什么 gprof 显示 flat profile 全是 __libc_start_main 或空白
这是最典型的链接/运行阶段失败信号:profiling 桩没被正确触发或符号丢失。
常见原因和验证方式:
立即学习“C++免费学习笔记(深入)”;
- 忘了加
-pg链接:即使编译用了-pg,链接时没传也会失效;检查nm a.out | grep mcount,应有U mcount(未定义引用) - 程序异常退出(如 crash、
exit()、signal 终止):gprof 依赖进程正常返回才能 flush 计数到gmon.out - 多线程下只统计主线程:gprof 本身不支持 pthread 的准确调用关系,
pthread_create后的函数不会出现在 call graph 中
gprof 输出里 self 和 children 时间对不上怎么办
self 是函数自身指令耗时(不含调用子函数时间),children 是它所有直接子调用的总耗时。两者相加≈该函数在调用链中的总贡献,但浮点累加误差、采样抖动、内联/尾调用优化都会让数字不严格守恒。
真正要注意的是:
- 如果
self极低但children很高,说明热点在下游——顺着 call graph 往下挖,别死盯顶层函数 -
called列显示调用次数,结合self可算单次开销,比总耗时更能定位低效逻辑 - 忽略
time百分比绝对值,关注排序:排前三的函数才值得花时间看
有没有比 gprof 更准的替代方案(尤其对现代 C++)
有,gprof 是 1980 年代设计的采样+插桩混合模型,对模板实例化、RAII、move 语义、inline lambda 等现代 C++ 特性支持弱,且无法区分 CPU-bound 和 cache-miss 瓶颈。
更实用的选择:
- Linux 下优先用
perf record -g ./a.out && perf report -g:基于硬件 PMU,开销低,支持 stack unwinding,能穿透std::vector::push_back看到具体内存分配点 - 需要火焰图:用
perf script | stackcollapse-perf.pl | flamegraph.pl > fg.svg,一眼识别深度嵌套中的长尾函数 - 怀疑内存问题?上
valgrind --tool=callgrind,虽然慢十倍,但能精确到行号,且支持callgrind_annotate交叉对比
gprof 现在更适合验证“某个函数是否真的 hot”,而不是定位“为什么 hot”——后者得靠 perf 或 VTune 这类能关联 L1d-cache-misses、branch-misses 的工具。











