gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 16:04:36 26 4
gpt4 key购买 nike

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

最佳答案

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

但是,如果您很着急并且可以在调试器下手动中断程序,而它主观上很慢,那么有一种简单的方法可以找到性能问题。

只需暂停几次,每次查看调用堆栈。如果有一些代码浪费了一定百分比的时间,20% 或 50% 或其他什么,这就是您在每个样本的行为中捕获它的可能性。因此,这大致是您将看到的样本百分比。不需要有根据的猜测。如果您确实猜测问题是什么,这将证明或反驳它。

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

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

  • 在指令层面不总结,
  • 他们在递归的情况下给出了令人困惑的总结。

  • 他们还会说它只适用于玩具程序,而实际上它适用于任何程序,而且在更大的程序上似乎更有效,因为他们往往有更多的问题需要发现。他们会说它有时会发现一些不是问题的东西,但只有当你看到一些东西时才是真的。如果您在多个样本上看到问题,那就是真实存在的问题。

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

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

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

    我经常听到的另一个反对意见是:“它会随机停在某个地方,它会错过真正的问题”。
    这来自于对真正的问题是什么有一个先验的概念。
    性能问题的一个关键特性是它们不符合预期。
    抽样告诉您某事有问题,您的第一 react 是难以置信。
    这是很自然的,但您可以确定它是否发现了问题是真实存在的,反之亦然。

    添加了:让我对它的工作原理做一个贝叶斯解释。假设有一些指令 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)是 0.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的可能成本的估计。如果数据量很小,它并不能准确地告诉我们成本是多少,只能说它大到值得修复。

    另一种看待它的方式称为 Rule Of Succession
    如果你抛硬币 2 次,而且两次都正面朝上,这能说明硬币的可能重量吗?
    受人尊敬的回答方式是说它是 Beta 分布,平均值为 (number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%

    (关键是我们不止一次看到 I。如果我们只看到一次,除了 f> 0之外,并不能告诉我们太多。)

    因此,即使是非常少量的样本也可以告诉我们很多关于它看到的指令的成本。 (它会看到它们的频率,平均而言,与它们的成本成正比。如果取 n个样本, f是成本,那么 I将出现在 nf+/-sqrt(nf(1-f))个样本上。例如, n=10 ”、 f=0.3,即 3+/-1.4样本。)

    添加:为了直观感受测量和随机堆栈采样之间的区别:
    现在有分析器可以对堆栈进行采样,即使在挂钟时间,但结果是测量值(或热路径或热点,“瓶颈”可以轻松隐藏)。他们没有向您展示(他们很容易可以)是实际 sample 本身。如果您的目标是找到瓶颈,则您需要查看的瓶颈数量平均为 2 除以所需时间的分数。
    因此,如果需要 30% 的时间,则平均有 2/.3 = 6.7 个样本将显示它,而 20 个样本将显示它的几率为 99.2%。

    这是检查测量值和检查堆栈样本之间差异的现成插图。
    瓶颈可能是像这样的一个大 Blob ,也可能是许多小 Blob ,这没有区别。

    enter image description here

    测量是水平的;它会告诉您特定例程需要多少时间。
    采样是垂直的。
    如果有任何方法可以避免当时整个程序正在执行的操作,并且如果您在第二个示例中看到它,那么您已经找到了瓶颈。
    这就是与众不同的原因 - 查看花费时间的全部原因,而不仅仅是花费多少。

    关于c++ - 如何分析在 Linux 上运行的 C++ 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/375913/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com