我如何分析在Linux中运行的C ++代码?

我有一个C ++应用程序,运行在Linux上,我正在优化。 我怎样才能确定我的代码的哪些区域运行缓慢?


如果您的目标是使用分析器,请使用其中一个建议的分析器。

但是,如果您很匆忙,而且在主调慢的情况下可以在调试器中手动中断程序,则有一种简单的方法可以找到性能问题。

只需暂停几次,每次看看调用堆栈。 如果有一些代码浪费了一定比例的时间,20%或50%,或者其他什么,那就是你在每个样本上的行为中可以捕获它的概率。 所以这大致就是您将看到它的样本的百分比。 没有必要的猜测。 如果你猜猜问题是什么,这将证明或反驳它。

您可能有多个不同大小的性能问题。 如果你清理其中的任何一个,其余的将占用更大的比例,并且在随后的传球中更容易被发现。 这种放大效应在复合多个问题时会导致真正巨大的加速因素。

警告:程序员往往对这种技术持怀疑态度,除非他们自己使用它。 他们会说分析器会给你这些信息,但是只有当他们对整个调用堆栈进行采样,然后让你检查一组随机抽样时才是如此。 (摘要是洞察力丢失的地方。)调用图不会给你相同的信息,因为

  • 他们没有在指导层面进行总结,而且
  • 他们在递归的情况下给出了令人困惑的总结。
  • 他们也会说它只适用于玩具程序,实际上它适用于任何程序,而且它似乎对更大的程序更好,因为它们往往会遇到更多问题。 他们会说它有时会发现不是问题的东西,但只有当你看到某种东西时才是如此。 如果您在多个样本上看到问题,则是真实的。

    PS如果有一种方法可以在某个时间点收集线程池的调用栈样本,那么也可以在多线程程序中完成此操作,就像在Java中一样。

    PPS简单来说,软件中抽象层次越多,发现性能问题的原因越多(有机会得到加速),就越有可能发生这种情况。

    补充:它可能并不明显,但堆栈抽样技术在递归存在的情况下工作得很好。 原因是,通过删除指令可以节省的时间大概是包含它的样本的比例,而不管样本中可能发生的次数。

    我经常听到的另一个反对意见是:“它会随机停止某个地方,它会错过真正的问题”。 这是由于事先有一个真正的问题是什么概念。 性能问题的一个重要特征是他们违背了期望。 抽样告诉你有什么问题,你的第一反应是不相信。 这很自然,但你可以肯定,如果它发现它是真实的问题,反之亦然。

    增加:让我做一个贝叶斯解释它是如何工作的。 假设有一些指令I (呼叫或以其它方式),它是在调用堆栈的一些分数f的时间(以及因此成本那么多)。 为了简单起见,假设我们不知道f是什么,但假设它是0.1,0.2,0.3,... 0.9,1.0,并且每个这些可能性的先验概率是0.1,所以所有这些成本是平等的可能是先验的。

    那么,假设我们只取2堆样本,并且我们在两个样本上看到指令I ,指定观测值o=2/2 。 这给了我们对I的频率f的新估计,根据这个:

    Prior                                    
    P(f=x) x  P(o=2/2|f=x) P(o=2/2&&f=x)  P(o=2/2&&f >= x)  P(f >= x)
    
    0.1    1     1             0.1          0.1            0.25974026
    0.1    0.9   0.81          0.081        0.181          0.47012987
    0.1    0.8   0.64          0.064        0.245          0.636363636
    0.1    0.7   0.49          0.049        0.294          0.763636364
    0.1    0.6   0.36          0.036        0.33           0.857142857
    0.1    0.5   0.25          0.025        0.355          0.922077922
    0.1    0.4   0.16          0.016        0.371          0.963636364
    0.1    0.3   0.09          0.009        0.38           0.987012987
    0.1    0.2   0.04          0.004        0.384          0.997402597
    0.1    0.1   0.01          0.001        0.385          1
    
                      P(o=2/2) 0.385                
    

    最后一列说,例如, f > = 0.5的概率是92%,高于前面的60%。

    假设先前的假设是不同的。 假设我们假设P(f = 0.1)为.991(几乎可以肯定),并且所有其他可能性几乎不可能(0.001)。 换句话说,我们以前的确定性是I便宜。 然后我们得到:

    Prior                                    
    P(f=x) x  P(o=2/2|f=x) P(o=2/2&& f=x)  P(o=2/2&&f >= x)  P(f >= x)
    
    0.001  1    1              0.001        0.001          0.072727273
    0.001  0.9  0.81           0.00081      0.00181        0.131636364
    0.001  0.8  0.64           0.00064      0.00245        0.178181818
    0.001  0.7  0.49           0.00049      0.00294        0.213818182
    0.001  0.6  0.36           0.00036      0.0033         0.24
    0.001  0.5  0.25           0.00025      0.00355        0.258181818
    0.001  0.4  0.16           0.00016      0.00371        0.269818182
    0.001  0.3  0.09           0.00009      0.0038         0.276363636
    0.001  0.2  0.04           0.00004      0.00384        0.279272727
    0.991  0.1  0.01           0.00991      0.01375        1
    
                      P(o=2/2) 0.01375                
    

    现在它说P(f> = 0.5)是26%,高于前面的0.6%。 所以贝叶斯让我们更新我们的可能成本的估计I 。 如果数据量很小,它不会准确告诉我们成本是多少,只是它足够大才值得修复。

    另一种看待它的方式被称为继承规则。 如果您将硬币翻转两次,并且两次都会出现,那么可以告诉您硬币的可能重量是什么? 推荐的答案是说它是一个Beta分布,平均值(命中数+ 1)/(尝试次数+2)=(2 + 1)/(2 + 2)= 75%。

    (最关键的是,我们看到I不止一次。如果我们只看到过一次,这并没有太大的告诉我们,除了f > 0)

    因此,即使只有极少数的样本可以告诉我们很多关于它所看到的指令的成本。 (平均来看,它们的频率与它们的成本成正比,如果取n样本, f是成本,那么I将出现在nf+/-sqrt(nf(1-f))样本上。 , n=10f=0.3 ,即3+/-1.4样品。)


    ADDED,为测量和随机堆栈采样之间的差异提供直观的感受:
    即使在挂钟时间,现在还有一些探测器可以对栈进行采样,但是出现的是测量结果(或热路径或热点,“瓶颈”很容易隐藏起来)。 他们没有向你展示(他们很容易就可以)是他们自己的实际样本。 如果你的目标是找到瓶颈,那么你需要看到的数量平均为2,除以所花费的时间。 因此,如果需要30%的时间,2 / .3 = 6.7个样本平均会显示出来,而20个样本显示的可能性为99.2%。

    以下是检查测量结果与检查堆栈样本之间差异的非常规说明。 瓶颈可能是这样一个大块,或许多小块,它没有什么区别。

    在这里输入图像描述

    测量是水平的; 它会告诉你具体例程所花的时间是多少。 采样是垂直的。 如果有什么方法可以避免整个程序在那个时候正在做什么,并且如果你在第二个样本上看到它,你就发现了瓶颈。 这就是造成这种差异的原因 - 看到时间消耗的全部原因,而不仅仅是多少。


    您可以使用Valgrind和以下选项

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

    它会生成一个名为callgrind.out.x的文件。 然后可以使用kcachegrind工具读取该文件。 它会给你一个结果的图形分析,比如哪条线要花多少钱。


    我假设你正在使用GCC。 标准解决方案是使用gprof进行配置。

    分析前请确保将-pg添加到编译中:

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

    我还没有尝试过,但我听说过有关google-perftools的好消息。 这绝对值得一试。

    相关问题在这里。

    如果gprof不为你工作,还有其他一些流行语:Valgrind,Intel VTune,Sun DTrace。

    链接地址: http://www.djcxy.com/p/1049.html

    上一篇: How can I profile C++ code running in Linux?

    下一篇: What are the rules about using an underscore in a C++ identifier?