C++ – 如何分析在Linux上运行的C++代码?

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


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

但是,如果你很匆忙,并且你可以手动中断调试器下的程序,而主观速度很慢,那么可以通过一种简单的方法来查找性能问题。

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

您可能会遇到不同大小的多个性能问题。如果你清除其中任何一个,剩下的将占用更大的百分比,并且在随后的传球中更容易发现。当在多个问题上复合时,这种放大效应可以导致真正大规模的加速因子。

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

  1. 他们没有在教学层面总结,并且
  2. 它们在递归的情况下给出令人困惑的摘要。

他们还会说它只适用于玩具程序,实际上它适用于任何程序,并且它似乎在更大的程序上更好地工作,因为它们往往有更多的问题要找。他们会说它有时会发现不是问题的东西,但只有在你看到一次之后才会这样。如果您在多个样本上看到问题,那就是真实的。

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

PPS作为一个粗略的概括,您在软件中拥有的抽象层越多,您就越有可能发现这是性能问题的原因(以及获得加速的机会)。

补充:它可能不是很明显,但堆栈采样技术在递归的情况下同样有效。原因是通过删除指令节省的时间通过包含它的样本的分数来近似,而不管样本中可能出现的次数。

我经常听到的另一个反对意见是:“ 它会在某个地方随机停止,它会错过真正的问题 ”。这来自对现实问题的先验概念。性能问题的一个关键属性是他们无视期望。抽样告诉你一些问题,你的第一反应是难以置信。这很自然,但你可以确定它是否发现问题是真的,反之亦然。

补充:让我对其工作原理进行贝叶斯解释。假设有一些指令I(调用或其他)在调用堆栈中占用了一小部分f时间(因此成本太高)。为简单起见,假设我们不知道是什么f,但假设它是0.1,0.2,0.3,…… 0.9,1.0,并且每种可能性的先验概率为0.1,因此所有这些成本同样可能先验。

然后假设我们只采集2个堆栈样本,并且我们看到I两个样本的指令,指定观察o=2/2。这给了我们对频率f的新估计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 | o=2/2)

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 | o=2/2)

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。如果数据量很小,它并不能准确地告诉我们成本是多少,只是它足够大,值得修复。

另一种看待它的方法叫做继承规则。如果你将硬币翻了2次,并且两次都出现了硬币,那么它对硬币的可能加权有什么影响呢?值得尊重的回答方式是说它是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样品)。


添加,以直观地感受测量和随机堆栈采样之间的差异:
现在有一些分析器可以对堆栈进行采样,即使是在挂钟时间,但是出现的是测量(或热路径,或热点,从中得到)一个“瓶颈”很容易隐藏)。他们没有告诉你(他们很容易)你自己的实际样本。如果您的目标是找到瓶颈,那么您需要查看的数量平均为 2除以所需的时间。因此,如果需要30%的时间,平均2 / .3 = 6.7个样本将显示它,并且20个样本显示它的机会是99.2%。

以下是检查测量和检查堆叠样本之间差异的袖口图示。瓶颈可能是这样的一个大块,或许多小块,它没有任何区别。

测量是水平的; 它告诉你特定例程所花费的时间。采样是垂直的。如果有任何方法可以避免整个程序在那一刻所做的事情,如果你在第二个样本上看到它,你就找到了瓶颈。这就是产生差异的原因 – 看到花费时间的全部原因,而不仅仅是花费多少。

添加评论

友情链接:蝴蝶教程