3

我第一次尝试使用 Callgrind/Kcachegrind 来分析我的 C++ 应用程序,我注意到需要更多时间的两个函数是:

  1. < 循环 1 > (50% 自我) 和
  2. do_lookup_x (15% 自我)

现在,根据我的理解,周期 1 与递归调用函数所用时间的估计有关,但我不太清楚我应该如何解释在这里花费的如此长的时间。如果有一些周期,我想看看哪个函数被调用得更频繁,最后占用更多的 CPU 时间。如果我禁用循环检测(视图->循环检测),那么循环 1 会消失,但“自我”时间总计约为 60%,我不确定这是最好的做法。关于 do_lookup_x 我完全一无所知......

你能澄清一下我应该如何解释这些结果吗?

提前致谢。

4

2 回答 2

2

在 KCachegrind 中可能会错误地检测到循环:http: //valgrind.org/docs/manual/cl-manual.html#cl-manual.cycles

6.2.4. 避免循环 通俗地说,循环是一组以递归方式相互调用的函数。...

循环本身并不坏,但往往会使代码的性能分析变得更加困难。这是因为周期内调用的包容性成本是没有意义的。包含成本的定义,即函数的自身成本加上被调用者的包含成本,需要函数之间的拓扑顺序。对于循环,这并不成立:循环中函数的被调用者包括函数本身。因此,KCachegrind 进行循环检测并跳过循环内调用的任何包含成本的可视化。此外,一个循环中的所有函数都被折叠成称为循环 1 的人工函数。

现在,当一个程序暴露出非常大的循环时(对于某些 GUI 代码,或者在使用基于事件或回调的编程风格的一般代码中也是如此),您将失去很好的属性,让您可以通过跟踪来自 main 的调用链来查明瓶颈,引导通过包容性成本。此外,KCachegrind 失去了显示调用图有趣部分的能力,因为它使用包容性成本来切断不感兴趣的区域。

尽管周期中的包容性成本毫无意义,但可视化的一大缺点激发了暂时关闭 KCachegrind 中的周期检测的可能性,这可能导致误导性的可视化。但是,由于独立调用链的不幸叠加,通常会出现循环,以使配置文件结果会看到一个循环。以非常小的包容性成本忽略无趣的电话会打破这些循环。在这种情况下,通过不检测循环来错误处理循环仍然会提供有意义的分析可视化。

尝试在 KCachegrind 的 View 菜单中关闭 Cycle Detection 并检查“Self”时间列,因为“Incl”将不正确。

您还可以尝试其他一些具有精确和完整功能堆栈保存的分析器。https://github.com/jrfonseca/gprof2dot脚本支持的许多分析器保存完整的堆栈,而不仅仅是 callgrind/cachegrind 格式中的被调用者-调用者对。

于 2017-06-18T05:06:16.603 回答
1

我同意@osgx 的观点,即您需要一个不同的分析器,它可以捕获整个调用堆栈。

那么,函数的包含时间百分比是一个非常简单的数字。它只是该函数出现的堆栈样本的一部分,无论它在单个样本中出现多少次。

这是一种思考方式。
- 假设每 10 毫秒采样一次,总共 100 秒,或 10,000 个样本。
- 假设函数 Foo 出现在 30% 的样本中,一次或多次。
- 这意味着如果您可以更改 Foo 使其几乎不需要时间,例如通过将其传递给一个非常快的子处理器,那么没有样本会看到它,因为它永远不会在堆栈上足够长的时间让样本打它。
- 所以这 30% 的样本会简单地消失,程序将花费 70 秒而不是 100 秒。
- 这意味着 Foo 个人负责 30% 的时间(不管递归)。

实际上,我更喜欢这种方法,因为我更感兴趣的是找出问题所在,而不是需要 29% 还是 31%。它需要任何东西,它所需要的东西不会受到测量精度的影响。

于 2017-06-19T21:15:20.003 回答