如何找到在Linux上运行的C++应用程序中运行缓慢的代码区域?


当前回答

使用Valgrind、callgrind和kcachegrind:

valgrind --tool=callgrind ./(Your binary)

生成callgrind.out.x。使用kcachegrind读取它。

使用gprof(add-pg):

cc -o myprog myprog.c utils.c -g -pg 

(对于多线程、函数指针不太好)

使用google perftools:

使用时间采样,可以发现I/O和CPU瓶颈。

英特尔VTune是最好的(出于教育目的免费)。

其他:AMD Codeanalysis(已被AMD CodeXL取代)、OProfile、“perf”工具(apt-get-install-linux工具)

其他回答

这是对Nazgob Gprof回答的回应。

过去几天我一直在使用Gprof,已经发现了三个重要的限制,其中一个是我在其他地方还没有看到过的:

它不能在多线程代码上正常工作,除非您使用变通方法调用图被函数指针弄糊涂了。示例:我有一个名为multithread()的函数,它使我能够在指定的数组上对指定的函数进行多线程处理(两者都作为参数传递)。然而,Gprof将所有对多线程()的调用视为等效的,以计算在孩子身上花费的时间。由于我传递给多线程()的一些函数花费的时间比其他函数长得多,所以我的调用图基本上是无用的。(对于那些想知道线程是否是这里的问题的人来说:不,多线程()可以选择,在这种情况下,只在调用线程上按顺序运行所有内容)。这里说“……调用数数字是通过计数而不是采样得出的。它们是完全准确的……”。然而,我发现我的调用图给了我5345859132+784984078作为对我调用最多的函数的调用统计数据,其中第一个数字应该是直接调用,第二个递归调用(都来自它本身)。因为这意味着我有一个bug,所以我在代码中加入了长(64位)计数器,并再次运行相同的程序。我的计数:5345859132个直接调用和78094395406个自递归调用。这里有很多数字,所以我要指出,我测量的递归调用是780亿,而Gprof是7.84亿:相差100倍。两次运行都是单线程和未优化的代码,一次是编译的-g,另一次是-pg。

这是在64位Debian Lenny下运行的GNUGprof(Debian的GNUBinutils)2.18.0.20080103,如果这对任何人都有帮助的话。

在工作中,我们有一个非常好的工具,它可以帮助我们监控我们想要的日程安排。这已多次有用。

它是用C++编写的,必须根据您的需要进行定制。不幸的是,我不能共享代码,只有概念。您使用一个包含时间戳和事件ID的“大”易失性缓冲区,可以在死后或停止日志系统后转储(例如,将其转储到文件中)。

您检索包含所有数据的所谓大缓冲区,一个小接口解析它并显示带有名称(up/down+value)的事件,就像示波器使用颜色(在.hpp文件中配置)所做的那样。

您可以自定义生成的事件数量,以仅关注您所需的内容。它帮助我们解决了调度问题,同时根据每秒记录的事件数量消耗了所需的CPU数量。

您需要3个文件:

toolname.hpp // interface
toolname.cpp // code
tool_events_id.hpp // Events ID

其概念是在tool_events_id.hpp中定义如下事件:

// EVENT_NAME                         ID      BEGIN_END BG_COLOR NAME
#define SOCK_PDU_RECV_D               0x0301  //@D00301 BGEEAAAA # TX_PDU_Recv
#define SOCK_PDU_RECV_F               0x0302  //@F00301 BGEEAAAA # TX_PDU_Recv

您还可以在toolname.hpp中定义一些函数:

#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
// ...

void init(void);
void probe(id,payload);
// etc

代码中可以使用的任何位置:

toolname<LOG_LEVEL>::log(EVENT_NAME,VALUE);

probe函数使用几条装配线尽快检索时钟时间戳,然后在缓冲区中设置一个条目。我们还有一个原子增量来安全地找到存储日志事件的索引。当然,缓冲区是圆形的。

希望这个想法不会因为缺少示例代码而混淆。

这是我用来加速代码的两种方法:

对于CPU绑定的应用程序:

在DEBUG模式下使用探查器来识别代码中有问题的部分然后切换到RELEASE模式,注释掉代码中有问题的部分(不加任何内容),直到看到性能的变化。

对于I/O绑定应用程序:

在RELEASE模式下使用探查器来识别代码中有问题的部分。


N.B.

如果你没有剖析器,就用穷人的剖析器。调试应用程序时单击暂停。大多数开发人员套件将使用注释的行号分解成程序集。从统计上看,你很可能会在一个消耗了大部分CPU周期的区域着陆。

对于CPU来说,在DEBUG模式下进行评测的原因是,如果您尝试在RELEASE模式下进行剖析,编译器将减少数学、矢量化循环和内联函数,这些函数在汇编代码时会使代码陷入无法映射的混乱。无法映射的混乱意味着您的探查器将无法清楚地识别所需的时间,因为程序集可能与正在优化的源代码不符。如果您需要RELEASE模式的性能(例如,对时间敏感),请根据需要禁用调试器功能以保持可用的性能。

对于I/O绑定,探查器仍然可以在RELEASE模式下识别I/O操作,因为I/O操作要么在外部链接到共享库(大多数情况下),要么在最坏的情况下会导致系统调用中断向量(探查器也很容易识别)。

您可以使用iprof库:

https://gitlab.com/Neurochrom/iprof

https://github.com/Neurochrom/iprof

它是跨平台的,允许您不实时测量应用程序的性能。您甚至可以将其与实时图表相结合。完整免责声明:我是作者。

还值得一提的是

HPC工具包(http://hpctoolkit.org/)-开源,适用于并行程序,并具有一个GUI,可通过该GUI以多种方式查看结果英特尔VTune(https://software.intel.com/en-us/vtune)-如果你有英特尔编译器,这很好τ(http://www.cs.uoregon.edu/research/tau/home.php)

我使用过HPCToolkit和VTune,它们在寻找帐篷中的长极点方面非常有效,并且不需要重新编译代码(除了必须在CMake中使用-g-O或RelWithDebInfo类型的内置来获得有意义的输出)。我听说TAU的能力类似。