C/C++性能分析工具

前言:之前写过一篇文章C++编程技巧 ------ 提高时空效率_3857. 休息时间c++-CSDN博客,关于如何使用C++的编程技巧来提升程序性能。有童鞋可能会问,那我们如何去定量地测试我们写的代码的性能,找到瓶颈所在呢?本文给大家推荐一些C/C++常用的性能分析工具。

目录

一、C/C++性能分析工具概览

[1.1 gprof (GNU Profiler)](#1.1 gprof (GNU Profiler))

1)机制

2)如何使用

3)示例报告解析

[1.2 Valgrind 工具集](#1.2 Valgrind 工具集)

[1)Memcheck -- 内存检查工具](#1)Memcheck – 内存检查工具)

[2)Callgrind -- 性能分析工具](#2)Callgrind – 性能分析工具)

[1.3 perf -- Linux内核性能分析器](#1.3 perf – Linux内核性能分析器)

1)机制

2)如何使用

3)高级功能

[1.4 Visual Studio Profiler](#1.4 Visual Studio Profiler)

1)机制

2)如何使用

3)示例报告解析

[1.5 Google Performance Tools (gperftools)](#1.5 Google Performance Tools (gperftools))

[1)CPU Profiler](#1)CPU Profiler)

[2)Heap Profiler](#2)Heap Profiler)

三、一些注意事项

[3.1 编译优化的影响](#3.1 编译优化的影响)

[3.2 分析工具本身的开销](#3.2 分析工具本身的开销)


一、C/C++性能分析工具概览

C/C++性能分析工具种类繁多,涵盖了从简单的命令行工具到功能强大的图形化软件工具。这里我们按类别分别介绍一些主流的性能分析工具。

1.1 gprof (GNU Profiler)

gprof是GNU编译器提供的经典性能分析工具,用于分析C/C++程序的函数调用与CPU使用时间。

1)机制

它通过在编译时插入统计代码,并在程序运行时收集数据,从而计算出每个函数的调用次数、自身执行时间以及总执行时间(包括调用子函数的时间)等信息。gprof生成的报告可以帮助开发者快速定位占用CPU时间最多的"热点函数",从而有针对性地进行优化。

2)如何使用

使用gprof比较简单,只需在编译和链接时加入**-pg**选项,然后运行程序,即可生成最终的分析数据。操作步骤如下:

bash 复制代码
# 编译程序,加入gprof分析支持后缀
g++ -pg -o myCode myCode.cpp

# 运行程序(程序结束时会在当前目录下生成gmon.out文件)
./myCode

# 使用gprof分析gmon.out文件,并将结果输出到文本文件
gprof myCode gmon.out > gprof.txt

gprof报告通常包括一个Flat Profile (各函数的自身时间和累计时间)和一个Call Graph(函数调用关系图)

3)示例报告解析
bash 复制代码
Flat profile:

Each sample counts as 0.02 seconds.
%   cumulative   self              self     total
time   seconds   seconds  calls  Ts/call  Ts/call  name
12.34      0.14     0.14     123     0.00     0.14  function_A
...

通过上述示例,可以看出,function_A函数自身占用了0.12秒的CPU时间,占程序总运行时间的12.34%。

1.2 Valgrind 工具集

Valgrind 是一套功能强大的开源动态二进制分析和检测工具集,主要用于调试和性能分析。它本身包含多个工具,其中最经典的是用于内存检查的Memcheck 和用于性能分析的Callgrind。通过Valgrind工具,开发者可以检测内存泄漏、非法内存访问等问题,同时也可以收集程序运行时的详细性能数据。

1)Memcheck -- 内存检查工具

Memcheck工具专门用于检测内存相关的错误,例如内存泄漏、访问未初始化的内存、读写越界等。

机制

它通过在模拟的CPU环境中运行程序,对每次内存访问进行检查,从而发现许多C/C++程序中常见的内存问题。当Memcheck发现问题时,会输出详细的错误信息,包括出错的内存地址、试图执行的非法操作类型,以及调用堆栈等,帮助开发者快速定位问题。

如何使用

要使用Memcheck,只需在程序前加上valgrind --tool=memcheck即可运行。例如:

bash 复制代码
valgrind --tool=memcheck ./myCode

如果程序存在内存管理问题,Valgrind会在输出中高亮显示。例如,对于一段有内存泄漏的代码,Memcheck会在程序结束时给出泄漏报告

bash 复制代码
==12345== HEAP SUMMARY:
==12345==     in use at exit: 48 bytes in 1 blocks
==12345==   total heap usage: 1 allocs, 0 frees, 48 bytes allocated

==12345== LEAK SUMMARY:
==12345==    definitely lost: 48 bytes in 1 blocks
==12345==    indirectly lost: 0 bytes in 0 blocks
==12345==    possibly lost: 0 bytes in 0 blocks
==12345==    still reachable: 0 bytes in 0 blocks
==12345==    suppressed: 0 bytes in 0 blocks

上述输出表明程序在退出时有48字节、1块内存没有被释放。通过Memcheck工具,开发者能够轻松发现内存问题。

注意事项

Memcheck 工具非常强大,但同时也存在开销。由于它需要对每条指令进行模拟检查,程序运行速度会变慢,一般只在开发测试阶段使用。

2)Callgrind -- 性能分析工具

Callgrind 工具用于性能分析,它可以记录程序运行时的函数调用信息、每个函数的执行次数、缓存命中率、分支预测失败率等细粒度性能数据。它通过动态分析生成一个名为**callgrind.out.<pid>**的输出文件,其中包含了丰富的性能统计信息.

如何使用

bash 复制代码
valgrind --tool=callgrind ./myCode

输出类似如下文件:

bash 复制代码
callgrind.out.12345

使用callgrind_annotate命令进行查看

bash 复制代码
callgrind_annotate callgrind.out.12345

查看结果举例如下:

bash 复制代码
--------------------------------------------------------------------------------
Ir        file:myCode
--------------------------------------------------------------------------------
1,503,000  myCode.cpp:func2
      1,500  myCode.cpp:func1
     20,000  myCode.cpp:func3

上面例子可以看出func2占了大部分指令(或缓存)。

还可使用kcachegrind命令进行图形化查看(函数调用关系非常直观)

bash 复制代码
kcachegrind callgrind.out.12345

注意事项

CallgrindMemcheck一样,同样存在运行开销。它模拟了CPU和内存层次结构,程序运行时可能比平时慢数十倍,一般也只在内部开发测试阶段使用。

1.3 perf -- Linux内核性能分析器

perf是Linux内核自带的一款强大的性能分析工具,能够利用CPU的硬件性能计数器来收集各种系统级性能数据。

1)机制

与gprof和Valgrind不同,perf 不需要对程序进行特殊编译或修改,它直接在运行的程序上进行采样,开销非常小,适合在生产环境中对系统进行实时分析。perf可以分析CPU使用率、缓存命中率、分支预测失败、指令执行数等多种指标,从而帮助开发者全面掌握程序的性能特征。

2)如何使用

使用perf分析程序通常包括"记录"和"报告"两个阶段

bash 复制代码
# 1. 使用perf record运行程序并记录性能数据
perf record ./myCode

# 2. 程序运行结束后,使用perf report查看分析报告
perf report

perf record 指令,默认情况下,perf会以一定的频率对指令指针进行采样,记录调用栈等信息。当程序结束时,使用perf report 可以查看汇总的报告,其中列出了热点函数及其占用的CPU时间百分比。

3)高级功能

例如,可以使用perf top实时查看当前系统的热点函数,类似top命令但针对性能数据。perf annotate可以将采样数据注释到源代码的每条指令上,显示每条指令执行的次数,帮助定位具体的性能瓶颈行。

1.4 Visual Studio Profiler

Visual Studio Profiler是微软Visual Studio集成开发环境(IDE)中内置的性能分析工具,用于Windows平台上的C++应用程序。

1)机制

VS Profiler提供了一套完整的性能诊断功能,包括CPU使用分析、内存分配分析、线程分析等,可以帮助开发者快速定位性能瓶颈。直接集成在IDE中,使用Visual Studio Profiler无需离开熟悉的开发环境,非常适合Windows开发者使用。

2)如何使用
  1. **启用性能分析:**在Visual Studio中,选择"调试"菜单下的"性能探查器"(Performance Profiler)选项,或直接按快捷键<Alt+F2>打开,进入性能分析模式。
  2. **选择分析类型:**在弹出的性能探查器窗口中,可以选择要进行的分析类型,例如"CPU使用"、"Instrumentation(插桩)"。可以根据需要勾选或取消勾选某些选项,例如同时启用CPU和内存分析,或者只关注CPU。
  3. **运行分析:**点击启动按钮运行程序。Profiler会自动在后台收集性能数据。开发者可以像平时一样运行程序,执行各种操作以触发不同的代码路径。
  4. 查看结果: 程序运行结束后,Visual Studio会显示性能报告。开发者可以在IDE中直接查看函数调用列表调用树时间线等视图。通过这些视图,可以找到耗时最长的函数,展开调用树查看上下文,或者定位到具体的代码行。
3)示例报告解析

Visual Studio Profiler的输出非常直观。例如,在CPU使用报告中,会列出每个函数的总CPU时间自身时间。(以下输出结果仅展示一个模板例子,并不精确)

Name Module Total CPU [ms, %] Self CPU [ms, %]
CpuDemo.Program.FuncA CpuDemo.exe 840 ms, 84.0% 210 ms, 21.0%
System.Math.Sqrt System.Private.CoreLib.dll 330 ms, 33.0% 330 ms, 33.0%
System.Math.Log System.Private.CoreLib.dll 300 ms, 30.0% 300 ms, 30.0%
CpuDemo.Program.Main CpuDemo.exe 850 ms, 85.0% ~0 ms, ~0%
CpuDemo.Program.FuncB CpuDemo.exe ~0 ms, ~0% ~0 ms, ~0%

1.5 Google Performance Tools (gperftools)

Google Performance Tools (gperftools) 是Google开源的一套性能分析工具集,主要用于C++程序的CPU性能和内存使用分析。它包括几个核心工具:一个高速的线程缓存malloc实现(TCMalloc )、一个CPU分析器(CPU Profiler )和一个内存堆分析器(Heap Profiler )。gperftools的特点是高性能易用性,特别适合多线程和大型C++项目。

1)CPU Profiler

CPU Profiler可以对程序进行采样式的CPU性能分析。与gprof不同,它不需要重新编译程序,而是通过链接其库并在运行时启动/停止分析来实现。

它会以一定的频率对调用栈进行采样,记录程序执行过程中各个函数的运行时间分布。生成的分析数据可以使用pprof 工具进行可视化查看,类似于火焰图(Flame Graph)的形式。

如何使用

使用gperftools的CPU分析器,需要在编译和链接时加入gperftools库,并在代码中显式启动/停止分析。

cpp 复制代码
#include <gperftools/profiler.h>

int main() {
    // 启动CPU性能分析
    ProfilerStart("cpu_profile.prof");

    // ... 需要分析的代码 ...

    // 停止CPU性能分析
    ProfilerStop();

    return 0;
}

编译时需要链接gperftools的Profiler库:

bash 复制代码
g++ -o myCode myCode.cpp -lprofiler

运行后,会生成一个名为cpu_profile.prof的文件。使用pprof工具可以查看分析结果

bash 复制代码
pprof --text myCode ./cpu_profile.prof
2)Heap Profiler

Heap Profiler 是gperftools中用于分析堆内存使用的工具。它可以帮助开发者了解程序在运行时的内存分配情况,找出内存泄漏内存热点(频繁分配/释放的对象)。Heap Profiler通过替换默认的malloc/free实现,在每次分配/释放内存时记录调用栈和大小,从而生成一份详细的内存使用报告。

如何使用

使用Heap Profiler 需要在代码中包含**<gperftoos/heap-profiler.h>**并在分析开始和结束处调用相应的函数。

bash 复制代码
#include <gperftools/heap-profiler.h>

int main() {
    // 启动内存分析
    HeapProfilerStart("heap_profile");

    // ... 需要分析的代码 ...

    // 停止内存分析并生成报告
    HeapProfilerStop();

    return 0;
}

编译时同样需要链接Heap Profiler库:

bash 复制代码
g++ -o myCode myCode.cpp -ltcmalloc

程序运行结束后,Heap Profiler会生成一系列以.heap为后缀的文件,例如heap_profile.001.heap、heap_profile.002.heap等,每个文件对应一个快照。可以使用pprof来汇总这些快照并生成最终报告:

bash 复制代码
pprof --text --base=myCode ./heap_profile.*.heap

三、一些注意事项

在使用上述性能分析工具时,开发者可能会遇到一些常见的问题,例如:

3.1 编译优化的影响

不同的性能分析工具对编译优化的容忍度不同。例如,gprof需要编译时插入统计代码,因此如果使用**-O2**等优化选项编译,可能会丢失一些分析信息。相反,像perf这样的采样工具在优化代码上才能给出有意义的结果。使用工具时应了解其对编译选项的要求,并相应地调整配置。

3.2 分析工具本身的开销

某些工具(如Valgrind)本身会显著降低程序运行速度。如果分析过程中程序运行变慢,开发者可能误以为是性能问题而浪费时间调试。实际上,这是工具开销导致的。因此,在分析时还应注意区分真正的性能问题和工具带来的性能影响。

相关推荐
sheji34168 小时前
【开题答辩全过程】以 基于Java的智慧环卫垃圾收运管理系统设计与实现为例,包含答辩的问题和答案
java·开发语言
暮色_年华8 小时前
随想3:关于语音采集线程 使用 CFS 调度或者 SCHED_FIFO 的思考
c++
Flash.kkl8 小时前
Linux——线程的同步和互斥
linux·开发语言·c++
sunfove8 小时前
Python 面向对象编程:从过程式思维到对象模型
linux·开发语言·python
努力学习的小廉9 小时前
【QT(七)】—— 常用控件(四)
开发语言·qt
CoderCodingNo9 小时前
【GESP】C++六级考试大纲知识点梳理, (3) 哈夫曼编码与格雷码
开发语言·数据结构·c++
froginwe119 小时前
C 标准库 - `<errno.h>`
开发语言
黎雁·泠崖9 小时前
Java&C语法对比:分支与循环结构核心全解析
java·c语言
鹿角片ljp9 小时前
Java IO流案例:使用缓冲流恢复《出师表》文章顺序
java·开发语言·windows
纵有疾風起9 小时前
【Linux 系统开发】基础开发工具详解:自动化构建、版本控制与调试器开发实战
linux·服务器·开发语言·c++·经验分享·开源·bash