inclusive cost 是函数自身执行时间与其所有子调用开销的总和,反映整条调用链的综合负担;它比 self cost 更易暴露因高频调用或深层嵌套导致的性能瓶颈。

inclusive cost 就是“函数自己干的 + 它叫别人干的所有活”的总时间
它不是函数单次执行体内的指令数,而是从调用入口开始、一路向下穿透所有子调用链后累积下来的总开销。比如 main 的 inclusive cost 通常接近程序总耗时,因为几乎所有代码都挂在它的调用树底下。
常见错误现象:看到某个小工具函数(如 std::string::c_str())的 inclusive cost 特别高,就以为是它写得慢——其实它可能只被 log_message() 调用了 10 万次,真正耗时的是 log_message() 里反复拼接字符串+写磁盘的过程,而 c_str() 只是“背锅”出现在每条调用路径末端。
- inclusive cost 高 ≠ 函数本身低效,要结合
Calls列和调用者看上下文 - Callgrind 默认开启成本传播(
--callgrind-out-file=生成的文件天然含 inclusive 数据) - 用
callgrind_annotate查看时,默认排序依据就是 inclusive cost,加--inclusive=no才切回 self cost
为什么 inclusive cost 比 self cost 更容易暴露优化机会
self cost 只反映函数体内代码执行量,但很多性能瓶颈藏在“调用频次”和“调用深度”里。例如一个 config_get_string() 函数 self cost 很低,但如果它被 5 层深的循环嵌套反复调用,inclusive cost 就会滚雪球式放大——这时优化方向不是重写这个函数,而是缓存结果或提前退出循环。
使用场景:排查 Web 服务响应延迟时,先看 top 3 inclusive cost 的函数,再顺着它们的 Callee 列往下钻,往往比盯着单个热点函数更有效。
- 分支预测失败、cache miss 等底层事件也计入 inclusive cost,所以它不只是“时间”,更是“执行负担”的综合体现
- 如果某函数 inclusive cost 高但 self cost 接近 0,说明它纯属“中转站”,重点查它的调用者是否做了冗余调用
-
callgrind_control --dump-instr=yes可强制按指令粒度统计,但会显著拖慢运行速度,日常分析不建议开启
callgrind_annotate 输出里 inclusive cost 的数值怎么读
典型输出像这样:
100.00% (123456789) main 50.23% (62012345) parse_config 25.11% (30987654) handle_request 12.55% (15493827) db_query
括号里是绝对计数(默认为指令数),百分比是相对于顶层 main 的占比。注意:这些数字不是毫秒,而是 Callgrind 模拟执行的抽象单位,只用于横向比较。
- 百分比加起来不会是 100%,因为存在未进入 call graph 的初始化/清理代码(如 C++ 全局对象构造)
- 同一函数在不同调用路径下可能有多个独立计数行,需合并看总 inclusive cost
- 若某行显示
???而非函数名,通常是 inlined 函数或符号缺失,需编译时加-g -fno-omit-frame-pointer











