引言:为什么要对C++代码进行性能分析?
没有测量的优化就是猜测。即使是经验丰富的开发人员,也常常会错误地判断哪段代码运行最慢。性能分析是回答“程序究竟在哪里花费了最多时间?”这一问题的唯一科学方法。
在本文中,我们将介绍在Linux上对C++代码进行性能分析的实用工具:gprof、perf和Valgrind(Callgrind)。您将学习如何运行分析器、解读它们的报告以及找到真正的瓶颈。
1. 使用gprof进行性能分析
代码准备
gprof是最简单、最易用的分析器。要使用它,需要使用-pg标志编译程序:
g++ -pg -O2 main.cpp -o my_program运行程序。它的执行速度会比平时稍慢,但会创建一个包含性能分析数据的gmon.out文件。然后运行gprof:
./my_programgprof my_program gmon.out > report.txt分析示例
考虑一个包含两个函数的简单程序:
#include <iostream>#include <cmath>
void slowFunction() { double sum = 0.0; for (int i = 0; i < 100000000; ++i) { sum += std::sin(i) * std::cos(i); } std::cout << "Sum: " << sum << std::endl;}
void fastFunction() { int count = 0; for (int i = 0; i < 1000; ++i) { for (int j = 0; j < 1000; ++j) { count += (i * j) % 3; } } std::cout << "Count: " << count << std::endl;}
int main() { slowFunction(); fastFunction(); return 0;}运行gprof后,在报告中我们会看到一个包含每个函数执行时间的表格:
Flat profile:
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 99.0 1.98 1.98 1 1980.0 1980.0 slowFunction() 1.0 0.02 0.02 1 20.0 20.0 fastFunction()结论很明显:slowFunction()占用了99%的时间。瓶颈在于循环中调用了三角函数sin和cos。
gprof的局限性
- 仅适用于使用
-pg编译的程序 - 不显示内联函数的执行时间
- 不适用于多线程应用程序(未经额外配置)
2. 使用perf进行性能分析(Linux)
基本运行
perf是一个强大的内核级性能分析工具。它不需要重新编译程序,并且适用于任何二进制文件(即使是使用-O3优化编译的):
# 安装(如果没有)sudo apt install linux-tools-common linux-tools-generic
# 统计CPU周期进行性能分析perf stat ./my_program
# 记录事件以进行详细分析perf record ./my_programperf reportperf stat示例
对于同一个程序,perf stat将显示:
Performance counter stats for './my_program':
1,987.45 msec task-clock # 1.000 CPUs utilized 10 context-switches # 5.028 /sec 0 cpu-migrations # 0.000 /sec 105 page-faults # 52.832 /sec 5,234,567,890 cycles # 2.634 GHz 9,876,543,210 instructions # 1.89 insn per cycle 1,234,567,890 branches # 621.234 M/sec 10,000,000 branch-misses # 0.81% of all branches
1.987456789 seconds time elapsed关键指标