【问题标题】:Profiling C++ in the presence of aggressive inlining?在存在激进内联的情况下分析 C++?
【发布时间】:2011-01-06 11:15:45
【问题描述】:

我正在尝试使用 gprof 找出我的 C++ 程序将时间花在哪里。这是我的困境:如果我使用与发布版本相同的优化设置进行编译,几乎所有内容都会被内联,而 gprof 毫无帮助地告诉我,我 90% 的时间都花在了核心例程中,所有内容都被内联了。另一方面,如果我在禁用内联的情况下编译,程序运行速度会慢一个数量级。

当我的程序在启用内联的情况下编译时,我想知道从我的核心例程中调用的过程花费了多少时间。

我在四核 Intel 机器上运行 64 位 Ubuntu 9.04。我查看了 google-perftools,但这似乎不适用于 x86_64。不能在 32 位机器上运行。

是否有人对启用内联时如何更有效地分析我的应用程序有什么建议?

编辑:这是对我的问题的一些说明。如果最初不清楚,我深表歉意。

我想找出我的应用程序中花费的时间。分析我的优化构建导致 gprof 告诉我大约 90% 的时间花在 main 中,所有内容都内联。我在分析之前就已经知道了!

我想知道的是内联函数花费了多少时间,最好不要在我的构建选项中禁用优化或内联。在禁用内联进行分析时,应用程序的速度会慢一个数量级。这种执行时间的差异是一个方便的问题,但我不相信禁用内联构建的程序的性能配置文件将与启用内联构建的程序的性能配置文件强烈对应。

简而言之:有没有办法在不禁用优化或内联的情况下获得有用 C++ 程序的分析信息

【问题讨论】:

标签: c++ profiling


【解决方案1】:

代码运行速度变慢并不重要(当然,您的方便除外) - 分析器仍会告诉您在每个函数中花费的正确时间比例。

【讨论】:

  • 另外 10 个赞成票用于说明显而易见且没有增加任何价值 -- whee :P
  • 也许——但话又说回来,完全有可能情况并非如此。如果您将函数调用视为一棵树,那么所有子树都可以从内联中平等受益,这是理所当然的。实际上,这几乎总是错误的。这种差异在某些情况下是微不足道的,但在另一些情况下却相当显着。
  • 大多数分析器都是由硬件中断驱动的,并且包括其他噪声。
  • 如果内联能够更好地优化核心功能(这是很有可能的),那么没有内联的分析就会产生误导。
  • 我不相信在禁用内联的情况下编译的程序与编译器具有疯狂内联函数的程序在模整体运行时是等效的:内联可以进行进一步的优化,例如持续传播、循环提升、循环融合等
【解决方案2】:

使用 CPU 的高性能计时机制开发一些宏(例如,x86)——不依赖系统调用的例程,并将运行核心循环的单个线程绑定到特定 CPU( set the affinity)。您需要实现以下宏。

PROF_INIT //allocate any variables -- probably a const char
PROF_START("name") // start a timer
PROF_STOP() // end a timer and calculate the difference -- 
            // which you write out using a async fd

我在我感兴趣的每个函数中都放置了类似的东西,我确保宏将计时调用置于调用树的上下文中——这可能是最准确的分析方法。

注意:

此方法由您的代码驱动 -- 并且不依赖外部工具以任何方式窥探您的代码。当涉及到一小段代码时,窥探、采样和中断驱动的分析是不准确的。此外,您还想控制收集计时数据的地点和时间——比如在代码中的特定结构,比如循环、递归调用链的开始或大容量内存分配。

-- 编辑--

您可能对link from this answer to one of my questions 感兴趣。

【讨论】:

    【解决方案3】:

    我假设您想要做的是找出哪些代码行花费了您足以值得优化的成本。这与计时功能有很大不同。 You can do better than gprof.

    Here's a fairly complete explanation of how to do it.

    您可以手动完成,也可以使用可以提供相同信息的分析器之一,例如oprofileRotateRight/Zoom

    顺便说一句,内联只有在被内联的例程很小并且本身不调用函数,并且被调用的行在足够的时间内处于活动状态时才具有重要价值。

    至于调试和发布构建之间的数量级性能比,可能是由于许多因素,可能是内联也可能不是。您可以使用上面提到的stackshot 方法来确定在这两种情况下发生了什么。我发现调试构建可能会因为其他原因而变慢,例如递归数据结构验证。

    【讨论】:

    • (+1) 不错的信息,尤其是 stackshot 链接(它表示关键是捕获而不是采样),尽管所有工具似乎都是在测量而不是捕获——stackshot 也是如此。
    • @Hassan:是的。我认为这是一个重点,定位的精度,而不是测量的精度。 Oprofile 和 Zoom 在挂钟时间获取堆栈样本,它们可以根据语句为您提供包含该语句的样本百分比。如果他们从大量样本中给出它,那没关系,但并不是真正必要的。我个人是手动完成的,因为这样我还可以看到更多的状态信息,例如正在处理的数据。这通常是至关重要的信息。
    • 是的,我同意,我也手动完成这些工作——并且在分析游戏引擎/嵌入式工作/操作系统例程和 RTOS 内容方面的共识也几乎使用这种方法。
    • @Hassan:不开玩笑?男孩,他们一直非常安静。我敢肯定,在高性能图形中,每个周期都很重要。自从 78 年在 mini 上做汇编程序以来,我一直在使用这种方法。即便如此,硬件人员仍想为我提供用于性能调整的计时器。我说,谢谢,但不用谢。 “暂停”按钮和“单步”不仅是最简单的工具,而且是最有效的。
    • 我尝试的配置文件构建使用与我的发布构建相同的选项构建,除了添加了 -fno-inlining 和 -pg。我没有明确内联我的任何函数;我把它留给了编译器,假设它可以比我在决定什么适合内联方面做得更好。由于编译器正在执行所有内联,我什至无法找出哪些函数占用了时间——它们都被内联了。显式禁用内联的分析让我得到这个信息。找出哪些线路昂贵也很有用。
    【解决方案4】:

    您可以使用更强大的分析器,例如 Intel 的 VTune,它可以为您提供装配线级别的性能细节。

    http://software.intel.com/en-us/intel-vtune/

    它适用于 Windows 和 Linux,但确实要花钱...

    【讨论】:

    • 是的,这可以解决问题——理论上是这样。这些工具在 Windows 中非常不可靠——它们在 win2003 上给了我 BSOOD,在许多机器上给了我 xp,并且通常也会导致其他问题。
    【解决方案5】:

    valgrind 会更有帮助吗?

    结合KCachegrind GUI,它提供了一种免费且简单的方式来浏览适合内联代码的注释代码。 在这里你有一个非常简单的说明:http://web.stanford.edu/class/cs107/guide_callgrind.html

    【讨论】:

      【解决方案6】:

      您可以使用 gcov 为您提供逐行执行计数。这至少应该告诉您哪些内联函数是瓶颈。

      【讨论】:

      • 根据您的建议,我玩了一点 gcov。它给出了呼叫计数,这不足以告诉我什么需要花费大量时间。
      猜你喜欢
      • 2011-06-02
      • 2013-05-30
      • 2011-03-26
      • 2022-01-16
      • 1970-01-01
      • 2015-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多