C++ 性能剖析演示
用 perf 剖析一个制造了三个典型 C++ 热点的程序,展示从"函数级"到"行级"的定位流程。
文件
| 文件 | 作用 |
|---|---|
hotspot.cpp | 三个可辨识热点:虚函数派发(vtable 间接跳转)、std::string 反复拼接、unordered_map 查找 |
构建与运行
bash
make hotspot # g++ -O2 -g -fno-omit-frame-pointer
make perf # perf record + perf report
make report # perf annotate 行级热点服务器 perf 权限注意:/proc/sys/kernel/perf_event_paranoid 为 -1 时普通用户即可采样;若为 2 或更高,需 sudo sysctl kernel.perf_event_paranoid=-1 或 root 运行。
实测数据(2026-08-27,AMD EPYC 7K62 / GCC 10.2.1 / 内核 3.10)
perf report(431 samples,cpu-clock)
76.10% __libc_start_main
|--41.07%--map_work (unordered_map 查找,远超直觉预期)
|--8.82%--string_work (string 拼接)
|--8.12%--virtual_work (虚函数派发,被 -O2 去虚化压低)程序本意是三个热点各占 50% / 20% / 30%,实测却是 41% / 8.8% / 8.1%——编译器优化完全改变了性能画像:-O2 下 virtual_work 的虚函数调用被去虚化(devirtualization)成直接调用,占比跌到 8%;而 unordered_map 的哈希与桶遍历没法被优化,反超成为最大热点。
perf annotate(行级)
perf annotate 能精确到源文件行(-g 编译保留行号),展示每一行汇编对应的采样占比,定位到热点所在的循环体。
教学结论
- 别猜,测:直觉认为虚函数最贵,实测哈希查找最贵。
- 符号是命根:编译必须
-g -fno-omit-frame-pointer,否则火焰图全是问号。 - 优化级别改变画像:同一份代码 -O0 与 -O2 的热点分布可能完全不同。
复现
bash
scp -r demos/cpp-expert/perf-profiling-cpp/ chzhuo@<server>:~/perf_demo/
ssh chzhuo@<server> "bash ~/perf_demo/run-remote.sh"