我有一个C++应用程序,运行在Linux上,我正在优化过程中.我如何确定代码的哪些部分运行缓慢?

推荐答案

如果你的目标是使用剖析器,请使用其中一个建议.

然而,如果您很匆忙,并且可以在调试器下手动中断程序,而程序主观上运行缓慢,那么有一种简单的方法可以发现性能问题.

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

您可能有多个不同大小的性能问题.如果你清除了其中的任何一个,剩下的将占据更大的百分比,并且更容易在随后的传球中被发现.这magnification effect个问题,如果再加上多个问题,可能会导致真正巨大的加速因素.

Caveat:程序员往往对这种技术持怀疑态度,除非他们自己使用过.他们会说分析器会给你这些信息,但只有当他们对整个调用堆栈进行采样,然后让你判断一组随机样本时,这才是真的.(总结是失go 洞察力的地方.)调用图不会提供相同的信息,因为

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

他们还会说它只在玩具程序上有效,而实际上它在任何程序上都有效,而且在更大的程序上似乎效果更好,因为他们往往有更多的问题要找.他们会说,它有时会发现不是问题的东西,但只有当你看到once个东西时,这才是真的.如果你在不止一个样本上看到问题,那就是真的.

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

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

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

我经常听到的另一个反对意见是:"It will stop someplace random, and it will miss the real problem".

Added:让我来解释一下它是如何工作的.假设有一些指令I(调用或其他)在调用堆栈上,占时间的一小部分f(因此成本很高).为了简单起见,假设我们不知道f是什么,但是假设它是0.1,0.2,0.3...0.9,1.0,这些可能性的先验概率都是0.1,所以所有这些成本在先验上都是相同的.

然后假设我们只取两个堆栈样本,我们在两个样本上都看到指令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 | 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                

The last column says that, for example, the probability that f >= 0.5 is 92%, up from the prior assumption of 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%.因此,Bayes允许我们更新对I的可能成本的估计.如果数据量很小,它不会准确地告诉我们成本是多少,只是它足够大,值得修复.

另一种看待它的方式叫做Rule Of Succession.

(The key is that we see I more than once. If we only see it once, that doesn't tell us much except that f > 0.)

所以,即使是非常少的样本也可以告诉我们很多关于它看到的指令的成本.(平均来看,它们的出现频率与成本成正比.如果采集n个样本,f是成本,那么nf+/-sqrt(nf(1-f))个样本上会出现I个样本.例如,n=10f=0.3,即3+/-1.4个样本.)


Added:为了直观地感受测量和随机堆栈采样之间的区别:

下面是一个即兴演示,说明了判断测量值和判断堆栈样本之间的区别.

enter image description here

测量是水平的;它告诉你特定时间的例行程序需要多少时间.

Linux相关问答推荐

记录终端输入和输出的 Bash 脚本

命令行中使用`..`为什么会导致权限被拒绝错误?

线程创建会在 Linux 中触发页面错误吗?它与软脏 PTE 有什么关系?

在不编写任何代码的情况下,是否有一个命令可以检索当前 shell 的亲和力中的可用内核数?

使用ansible配置Linux VM使用vmware_vm_shell模块时变成su?

Ubuntu 20 不支持 MAP_FIXED_NOREPLACE

使用 sed linux 命令和 i sed 命令进行 preprend 时的反向引用

如何从 2 个文件中获取内容并使用 shell 脚本将该内容附加到新文件中

在 puppet 中管理 linux 的用户密码

Docker 容器立即退出

为什么 perf 不报告缓存未命中?

如何找出哪个进程正在消耗等待 CPU(即 I/O 阻塞)

使远程目录保持最新

如何搜索文件并将它们压缩到一个 zip 文件中

如何设置errno值?

Linux上C中的标准输出线程安全?

使用sudo apt-get install build-essentials

env和set(在 Mac OS X 或 Linux 上)有什么区别?

如何在我的终端中编辑文本文件

Linux 上的 NuGet:获取响应流时出错