Linux 上的 C++ 性能分析:加速代码的指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

你的 C++ 代码运行缓慢?是时候深入探究了

\n\n

你编写了一段乍看之下完美的 C++ 代码。编译无误,程序启动... 但运行起来却有点迟缓。熟悉吗?你恨不得立刻用汇编重写所有代码,或者赶紧添加多线程。别着急。问题很可能不在于架构,而在于几个消耗了 90% CPU 时间的“热点”函数。

\n\n

在 Linux 世界中,我们拥有强大的工具库用于 C++ 代码性能分析。这不是魔法,而是科学。我们不靠猜测,而是靠测量。在本指南中,我将向你展示四种实用的方法:从快速的“宏观概览”到微观基准测试。你将学会如何像真正的专家一样在 Linux 上分析 C++ 代码。

\n\n

为什么猜测是邪恶的,而性能分析是好的

\n\n

在深入代码之前,请记住优化的黄金法则:“不要优化未经测量的代码”。你的直觉是一个糟糕的顾问。我们常常花费数小时优化一个每分钟才调用一次的函数,却忽略了一个简单的千万次迭代循环。

\n\n

性能分析回答了三个主要问题:

\n
    \n
  • 程序在哪里花费了最多时间?
  • \n
  • 为什么会发生这种情况(算法效率低、大量 I/O 操作、缓存未命中)?
  • \n
  • 你的修改改变了什么?
  • \n
\n\n

今天,我们将介绍四种工具,它们能覆盖你 99% 的需求。开始吧!

\n\n

工具 #1:gprof — 经典之作

\n\n

gprof 是一个老牌、可靠的内置 GNU 性能分析器。它分两步工作:在程序运行时收集统计数据,然后进行分析。优点:无需安装额外软件,每个发行版都自带。缺点:结果近似,并且不能直接用于多线程程序。

\n\n

如何使用 gprof

\n\n

步骤 1. 使用特殊标志 -pg 编译程序:

\n
g++ -pg -O2 my_program.cpp -o my_program
\n\n

注意:我们使用了 -O2。对未优化的调试版本进行性能分析是没有意义的,你会得到失真的结果。

\n\n

步骤 2. 运行程序。由于 gprof 在收集数据,程序运行会稍慢一些。

\n
./my_program
\n\n

步骤 3. 在当前文件夹中会生成一个 gmon.out 文件。分析它:

\n
gprof my_program gmon.out > analysis.txt
\n\n

打开 analysis.txt,你会看到类似这样的内容:

\n
Flat profile:\nEach sample counts as 0.01 seconds.\n  %   cumulative   self              self     total\n time   seconds   seconds    calls  us/call  us/call  name\n 60.00      0.06     0.06    10000     6.00     6.00  slow_function\n 20.00      0.08     0.02     5000     4.00     4.00  fast_function\n 10.00      0.09     0.01                             main
\n\n

瞧!我们看到 slow_function 占用了 60% 的时间。这就是我们需要优化的候选函数。

\n\n

用于性能分析的代码示例

\n\n

让我们编写一个简单的程序,我们将用所有工具对其进行分析:

\n\n
#include <iostream>\n#include <cmath>\n\nvoid slow_function(int n) {\n    volatile double sum = 0.0; // volatile 防止编译器优化为零\n    for (int i = 0; i < n; ++i) {\n        for (int j = 0; j < 1000; ++j) {\n            sum += std::sin(i) * std::cos(j);\n        }\n    }\n    std::cout << sum << std::endl;\n}\n\nvoid fast_function(int n) {\n    volatile int count = 0;\n    for (int i = 0; i < n; ++i) {\n        count += i % 2;\n    }\n    std::cout << count << std::endl;\n}\n\nint main() {\n    slow_function(10000);\n    fast_function(50000);\n    return 0;\n}
\n\n

使用 -pg 标志编译并运行

博客

书籍推荐