gprof 分析 C++ 性能需编译时加 -pg 且建议 -O0 -g,运行生成 gmon.out 后用 gprof 解析,可定位 CPU 瓶颈;但不适用于 I/O、多线程及短时程序。

用 gprof 分析 C++ 程序性能,核心是编译时加 -pg,运行后生成 gmon.out,再用 gprof 解析。它能告诉你哪些函数耗时最多、被调用了多少次、调用关系如何,适合定位 CPU 密集型瓶颈。
编译和运行:必须带 -pg 且禁用优化干扰
gprof 依赖插桩(instrumentation),所以编译时要加 -pg,同时建议关闭优化(或仅用 -O1),否则函数内联、重排会影响调用图准确性:
-
错误示范:
g++ -O2 -pg main.cpp -o app→ 优化可能吞掉函数边界,调用统计失真 -
推荐做法:
g++ -g -O0 -pg main.cpp -o app(-g保留符号信息,方便显示函数名) - 运行一次程序:
./app→ 自动在当前目录生成gmon.out
生成报告:gprof 命令常用组合
直接运行 gprof app 会输出扁平化(flat)和调用图(call graph)两部分。常用增强方式:
- 显示函数名而非地址:
gprof app gmon.out(默认行为,前提是编译带-g) - 只看耗时 top 10:
gprof app gmon.out | head -n 50 - 排除库函数干扰(聚焦自己的代码):
gprof app gmon.out -q "your_namespace::|main" - 生成可读性更强的调用图:
gprof app gmon.out -q > callgraph.txt
读懂关键字段:重点关注 % time、calls、self ms/call
扁平化报告中三列最关键:
立即学习“C++免费学习笔记(深入)”;
- % time:该函数独占时间占总执行时间的百分比(不含子函数)→ 找“大头”
- calls:被调用次数 → 高频小函数也可能成瓶颈(比如循环里反复调用的 get())
- self ms/call:每次调用自身平均耗时(不含子函数)→ 判断单次开销是否异常
例如某 calculate() 占 42% 时间、调用 1 次、每次耗时 850ms,说明它内部有严重计算密集逻辑;而 vec_push_back() 占 18%、调用 10 万次,就要检查是否误在循环里重复分配或拷贝。
注意局限性:gprof 不适合测 I/O、多线程、短时程序
gprof 是基于计数器采样 + 插桩的混合机制,有明显约束:
- 无法准确反映系统调用(如
read()、sleep())耗时,这部分常被记为“未归因时间” - 不支持多线程(
-pg默认只跟踪主线程,其他线程的调用不会被记录) - 程序运行太短(gmon.out 可能为空或数据稀疏,结果不可靠
- 若函数被内联(即使没开
-O2,某些 trivial 函数仍可能被 GCC 内联),则不会出现在报告中
遇到上述情况,建议换用 perf(Linux)、vtune 或 google-perftools 等更现代工具。










