C++性能分析工具,perf, valgrind,gprof

  • gprof:这是一个GNU的性能分析工具,主要用于分析程序的函数调用关系,以及每个函数的运行时间等。
  • Valgrind:这是一个用于内存调试、内存泄漏检测以及性能分析的开源工具集。其中,Valgrind的Callgrind工具可以收集程序运行时的函数调用信息,用于性能分析。
  • perf:这是Linux下的一个性能分析工具,可以收集CPU使用情况、缓存命中率、分支预测错误等多种性能数据。

 


 

假设,你当前有一个程序代码,名字叫   hello.cpp


 

总结一下基本的使用:

假设测试文件是  hello.cpp,生成的文件是 myprogram

gprof :

##编译代码
g++ -pg -o myprogram hello.cpp

## 执行代码
./myprogram

## 分析日志
gprof myprogram gmon.out > analysis.txt

perf:

## 编译代码
g++ -O2 -o myprogram  hello.cpp


## 执行程序
perf record ./myprogram

## 分析结果
perf report

valgrind:

## 直接执行程序 进行检查(就是执行有点慢)
valgrind --tool=memcheck ./myprogram

## 检测内存泄漏
valgrind --leak-check=full ./hello

 

其他更多高级用法,还需要更多时间去解锁。这里只是开个头。

 

 

gprof

 

gprof 通常作为 GNU Binutils 的一部分,在大多数 Linux 发行版中都是默认安装的。

gprof 是一个GNU项目中的性能分析工具,用于分析C和C++程序的函数调用图和每个函数的CPU使用时间。它通过测量程序执行过程中的函数调用频率和运行时间来帮助你识别出那些占用了最多运行时间的函数,从而定位可能的性能瓶颈。

以下是使用 gprof 的基本步骤:

1. 编译程序以包含gprof分析

在编译你的程序时,你需要使用 -pg 选项来告诉编译器包含 gprof 的分析代码。例如:

g++ -pg -o myprogram hello.cpp

这将生成一个可执行文件 myprogram,其中包含了 gprof 所需的性能分析代码。

2. 运行程序

运行你的程序,就像平常一样。gprof 会收集运行时信息,并将这些信息写入一个名为 gmon.out 的文件中。

./myprogram

运行结束后,你会在当前目录下找到 gmon.out 文件。

3. 使用gprof分析数据

现在,你可以使用 gprof 工具来分析 gmon.out 文件中的数据。执行以下命令:

gprof myprogram gmon.out > analysis.txt

这将会生成一个名为 analysis.txt 的文本文件,其中包含了 gprof 的分析结果。这个文件包含了函数调用图、每个函数的调用次数、每个函数消耗的CPU时间等信息。

4. 分析结果

打开 analysis.txt 文件,你可以看到类似下面的输出:

Flat profile:  
  
Each sample counts as 0.01 seconds.  
  %   cumulative   self              self     total             
 time   seconds   seconds    calls  Ts/call  Ts/call  name      
...  
 12.34      0.12     0.12      123    0.00    0.12  function_name  
...

 

这个输出会按照函数消耗CPU时间的百分比排序。self 列显示了函数本身的执行时间,而 total 列显示了包括该函数调用的所有子函数在内的总执行时间。通过查看这些信息,你可以确定哪些函数消耗了最多的CPU时间,并考虑如何优化它们。

注意事项

  • gprof 主要用于分析CPU使用情况,对于I/O密集型或网络密集型的程序可能不是最佳选择。
  • gprof 的分析结果可能受到编译器优化、操作系统调度等因素的影响,因此结果可能不是完全准确的。
  • gprof 只能提供函数调用级别的信息,对于更细粒度的性能分析(如指令级别的分析),你可能需要使用其他工具,如 perf

尽管 gprof 是一个有用的工具,但它也有一些局限性。因此,对于复杂的性能问题,你可能需要结合使用多种工具和技术来得到更准确的诊断结果。

 

 


 

perf

 

在 CentOS 7 系统中,perf 工具通常已经预装在 kernel-tools 或 kernel-tools-libs 包中。如果没有预装,你可以通过 yum 包管理器来安装它。以下是使用 perf 来分析 C++ 程序性能的详细步骤和示例。

1. 安装 perf 工具

首先,确保你的系统已经安装了 perf 工具。你可以使用以下命令来安装:

sudo yum install kernel-tools kernel-tools-libs kernel-devel  perf

这将会安装 perf 以及其他内核工具。

2. 编译你的 C++ 程序

确保你的 C++ 程序是用优化过的编译选项编译的,因为未优化的代码通常不会给出有用的性能分析信息。使用 -O2 或 -O3 优化级别来编译你的程序。

g++ -O2 -o myprogram  hello.cpp

 

3. 使用 perf 分析程序性能

3.1. 使用 perf record 命令记录性能数据

使用 perf record 命令来运行你的程序,并收集性能数据:

perf record ./myprogram

 

这将会运行你的程序,并收集关于 CPU 使用情况、缓存命中、分支预测错误等性能数据。

3.2. 使用 perf report 命令查看分析结果

程序运行结束后,使用 perf report 命令来查看收集到的性能数据报告:

perf report

 

perf report 会展示一个交互式的文本界面,其中包含每个函数的 CPU 使用时间百分比、调用次数等信息。你可以使用箭头键来浏览不同的函数,按 Enter 键来深入查看特定函数的调用栈。

3.3. 其他 perf 命令选项

你还可以使用 perf 的其他命令和选项来进行更详细的性能分析,例如:

  • perf top:实时显示当前运行的程序的热点函数。
  • perf annotate:显示特定函数的源代码注释,包括每个代码行的执行次数。
  • perf stat:显示程序的总体性能统计信息,如指令数、缓存未命中次数等。

4. 分析结果

通过 perf report 或其他 perf 命令的输出,你可以识别出哪些函数或代码行占用了最多的 CPU 时间,从而定位性能瓶颈。你可以考虑优化这些瓶颈区域,比如通过减少循环次数、使用更有效的算法、减少内存分配等方式。

注意事项

  • 确保你的系统内核是支持 perf 的,并且与你的 perf 工具版本兼容。
  • perf 工具的准确性和有效性可能会受到操作系统调度、其他运行中的进程、系统负载等多种因素的影响。
  • 在分析性能时,考虑多次运行你的程序并收集数据,以获得更可靠的结果。

通过结合 perf 工具和其他性能优化技术,你可以有效地分析并改进 C++ 程序的性能。

 


 

Valgrind

 

Valgrind 是一个用于内存调试、内存泄漏检测以及性能分析的开源工具。它可以在程序运行时检测多种问题,如内存泄漏、未初始化的内存、使用已释放的内存等。

安装 Valgrind

在 CentOS 7 上,你可以使用 yum 包管理器来安装 valgrind

sudo yum install valgrind

 

使用 Valgrind

1. 内存调试与泄漏检测

假设你有一个名为 myprogram 的 C++ 程序,你可以使用 valgrind 的 memcheck 工具来检测内存问题:

valgrind --tool=memcheck ./myprogram

 

如果程序存在内存泄漏或未初始化的内存访问等问题,valgrind 会输出相应的警告信息。

2. 性能分析

valgrind 的 callgrind 工具可以用来收集程序运行时的函数调用信息,这对于性能分析非常有用。首先,你需要使用 callgrind 运行你的程序:

valgrind --tool=callgrind ./myprogram

 

运行结束后,callgrind 会生成一个名为 callgrind.out.<pid> 的文件,其中 <pid> 是程序的进程 ID。然后,你可以使用 kcachegrind 工具来查看和分析这些数据:

kcachegrind callgrind.out.<pid>

 

kcachegrind 是一个图形界面的性能分析工具,它可以展示函数调用图、每个函数的调用次数以及执行时间等信息。如果你的系统没有安装 kcachegrind,你可以使用 yum 来安装它:

sudo yum install kcachegrind

 

详细示例

假设你有一个简单的 C++ 程序 memory_example.cpp,它可能存在内存泄漏:

// memory_example.cpp  
#include <iostream>  
#include <vector>  
  
int main() {  
    std::vector<int>* vec_ptr = new std::vector<int>;  
    for (int i = 0; i < 100; ++i) {  
        vec_ptr->push_back(i);  
    }  
    // 注意:这里忘记释放 vec_ptr 指向的内存  
    return 0;  
}

 

首先,编译这个程序:

g++ -o memory_example memory_example.cpp

 

然后,使用 valgrind 的 memcheck 工具来检测内存泄漏:

valgrind --tool=memcheck ./memory_example

 

你可能会看到类似下面的输出:

==12345== Memcheck, a memory error detector  
==12345== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.  
==12345== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info  
==12345== Command: ./memory_example  
==12345==   
==12345==   
==12345== HEAP SUMMARY:  
==12345==     in use at exit: 408 bytes in 1 blocks  
==12345==   total heap usage: 1 allocs, 0 frees, 408 bytes allocated  
==12345==   
==12345== LEAK SUMMARY:  
==12345==    definitely lost: 408 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  
==12345== Rerun with --leak-check=full to see details of leaked memory  
==12345==   
==12345== For counts of detected and suppressed errors, rerun with: -v  
==12345== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

 

这个输出表明程序存在内存泄漏,因为它分配了一个 std::vector<int> 的内存但没有释放它。你可以通过添加 delete vec_ptr; 在 main 函数的末尾来修复这个问题。

注意事项

  • valgrind 的运行可能会使程序运行得比正常慢得多,

 

 

valgrind 继续补充:

valgrind --leak-check=full ./hello 这个命令是用来使用 valgrind 的 Memcheck 工具来运行名为 hello 的程序,并且以完全检查模式(full check mode)来检测内存泄漏。--leak-check=full 选项会告诉 valgrind 在程序结束时报告所有可能的内存泄漏,包括那些仍然可以通过指针访问到的内存(即“reachable”的内存块)。

valgrind 是一个非常强大的工具,它提供了多种检查和分析程序的选项和工具。以下是几个 valgrind 中比较常用的用法示例,并解释说明它们的用途:

1. 内存泄漏检测

valgrind --leak-check=full ./your_program
  • 用途:检测程序在运行过程中分配但未被释放的内存,帮助发现内存泄漏。
  • 选项说明:--leak-check=full 会报告所有可能的内存泄漏,包括那些仍然可访问的。

2. 堆栈跟踪

valgrind --tool=memcheck --trace-children=yes ./your_program
  • 用途:跟踪程序中函数的调用栈,可以帮助定位问题发生的源头。
  • 选项说明:--trace-children=yes 会跟踪由 your_program 创建的子进程的函数调用。

3. 缓存模拟

valgrind --tool=cachegrind --I1=32k,2,8 --D1=32k,2,8 ./your_program
  • 用途:模拟 CPU 缓存的行为,以评估程序对缓存的利用率和可能的性能瓶颈。
  • 选项说明:--I1 和 --D1 分别设置一级指令缓存和一级数据缓存的大小、关联性和替换策略。

4. 调用图分析

valgrind --tool=callgrind ./your_program

运行完毕后,可以使用 kcachegrind 或其他工具查看生成的调用图数据:

kcachegrind callgrind.out.<pid>
  • 用途:收集程序运行时的函数调用信息,生成调用图,用于性能分析和优化。
  • 说明:callgrind.out.<pid> 是 callgrind 工具生成的数据文件,其中 <pid> 是运行程序的进程ID。

5. 数据竞争检测

valgrind --tool=drd ./your_program
  • 用途:检测多线程程序中的数据竞争问题,即多个线程同时访问和修改同一内存位置而没有适当的同步。
  • 说明:drd 工具可以帮助发现多线程编程中的潜在问题。

这些只是 valgrind 提供的一些常用工具和选项的示例。valgrind 的功能非常强大,还包含其他工具,并且每个工具都有许多选项可以调整。为了充分利用 valgrind,建议查阅其官方文档或手册页(使用 man valgrind 命令)以获取更详细的信息和用法示例。

 

posted @ 2024-03-16 11:06  He_LiangLiang  阅读(3137)  评论(0编辑  收藏  举报