【问题标题】:Profiling the C++ compilation process分析 C++ 编译过程
【发布时间】:2012-11-13 15:11:20
【问题描述】:

我倾向于编写相当大的模板化仅标头 C++ 库,而我的用户通常抱怨编译时间。想了想,我突然想到我不知道时间都去哪儿了。是否有一些简单的方法可以使用通用编译器(例如 g++、icc 和 xlC)来分析 C++ 编译过程?例如,是否有可能知道在each of the phases of C++ compilation 中花费了多少时间?

【问题讨论】:

标签: c++ compilation profiling


【解决方案1】:

对于GCC有debugging options可以找到how much time is spent within each of the phases of C++ compilation?

-Q 使编译器在编译时打印出每个函数名称,并在完成时打印有关每次传递的一些统计信息。

-ftime-report 使编译器在完成时打印一些有关每次传递所消耗时间的统计信息。

通行证在GCCINT 9: Passes and Files of the Compiler 中进行了描述。

您可以在此处将带有-v -ftime-report 的单个源文件的g++ 编译输出发布到discuss it。 GCC mailing list 可能会有一些帮助。


对于GCC 以外的编译器(或 GCC比3.3.6 更古老的),请参阅此线程中的其他选项。

【讨论】:

  • PS:-Q 的输出可能会被一些 awk 或 perl 脚本抓取、解析和分析;或者您可以在控制台上观看函数名称打印,长时间暂停后打印的任何内容都很难编译。
  • 知道如何将时间附加到函数名称(缺少黑客 g++)吗?我有一个 200 MB 的文件,其中包含意大利面条式的函数,不知道哪个函数需要很长时间才能编译。它们大多编译速度很快,只有很多(它也是模板繁重的代码)。我在想一个管道和一个脚本,但是管道有一些缓冲区,并且在打印更多内容之前,具有短名称的函数可能无法到达那里。
  • 猪,尝试在 gcc/cgraphunit.c 和 gcc/toplev.c 中 grep 'quiet_flag' (announce_function - "当解析函数定义的开头时,此函数会在 stderr 上打印名称的功能”)。这个announce_function 可能是添加时间戳打印(gettimeofday)或将输出重写为某种无缓冲方式的点。或者另一种可能的方法是启用调试转储(-fdump-rtl-all-all-fdump-tree-all-all-fdump-ipa-all-all),但它们每次输出 1 个文件;您需要将它们转换为每次传递和每个函数输出 1 个文件(获取大量具有创建时间的文件)。
【解决方案2】:

Clang 9(和更新版本)有一个 -ftime-trace 标志,这使得它可以将分析报告输出为 JSON(除了一个目标文件)。

您可以将此文件导入 Chrome 随附的分析器 (chrome://tracing) 以获得漂亮的可视化效果:

这些条对应于必须解析的标题,并且对于每个标题,必须解析的特定类(可能还有其他构造)。它还报告实例化特定模板所花费的时间。

【讨论】:

    【解决方案3】:

    Boost 项目中的 a tool 可以用于几乎任何编译器和构建系统。

    该工具需要带有TEMPLATE_PROFILE_ENTER() 和TEMPLATE_PROFILE_EXIT() 宏调用的源代码检测。然后,这些宏在编译时生成特定的诊断(警告),这些诊断与实例化调用堆栈(因此允许构建和visualizing 调用图)一起由脚本定时和收集。不错,IMO。

    不过我还没用过。

    【讨论】:

    • 在其文档页面中,我认为不需要源代码检测。你在哪里读到的?
    • @Irineau,在源代码中。该工具还提供了一些脚本,这些脚本似乎可以自动执行检测(尽管粒度未知)。
    • 链接已失效。
    • 嗯@rustyx 这也难怪,在 URL 中看到 svn.boost.org 和 21 世纪的时钟......有人上传了 fork/mirror/rewrite? 的不过,也许这会有所帮助。
    【解决方案4】:

    我还没有尝试过,但是 templight 看起来很有希望:https://github.com/mikael-s-persson/templight

    【讨论】:

    • 不幸的是,这需要从源代码修补和构建clang。不是世界末日,而是一个公平的承诺(假设补丁甚至适用)
    【解决方案5】:

    你可以在某种程度上将它们分开(我假设make)

    • 添加一个仅预处理文件的构建规则(使用-E 开关),以及一个依赖于预处理器输出文件的.PHONY 目标,就像普通二进制目标依赖于.o 文件一样。衡量构建此目标需要多长时间
    • 添加一个依赖于所有.o 文件但不链接它们的'PHONY 目标。衡量构建这个目标需要多长时间(从干净开始)
    • 测量干净构建普通二进制文件需要多长时间

    现在您已经知道预处理、编译和链接需要多长时间了。您还可以比较第二个和第三个目标的优化和非优化 (-O0) 版本,看看在优化器中花费了多长时间。

    【讨论】:

    • 感谢您的回复。我认为这对于 C 程序来说已经绰绰有余了,但是对于不构建多个 .o 文件的仅包含头文件的 C++,几乎所有时间都将花费在构建单个 .o 上。我赞成,但我会祈祷有人会提出更细粒度的方法。
    • 啊,所以你对翻译阶段不感兴趣,因为哪一段代码花费的时间最多?
    • 如果您使用 clang/llvm,您可以使用类似的技术将前端 (clang) 与后端 (llvm-opt) 分开。在后端,您甚至可以转储优化器图并单独运行它们。在 gcc 中,您可以比较 -O0 和 -O3 之间的构建时间,并查看优化所用时间与其他地方所用时间之间的差异。然后,您可以有选择地启用优化器以查看哪个是最严重的违规者(如果有的话)。
    【解决方案6】:

    strace -e trace=process -f -r -ttt -T 上的某些变体可能会吸引您,至少对于像 g++ 这样被分解为多个进程的编译器而言。

    【讨论】:

      【解决方案7】:

      其他人已经为 GCC 建议了-ftime-report 命令行标志,这使得编译器打印一些关于每个编译阶段消耗的时间的统计信息。缺点是它只显示一个单元的摘要。

      我编写了一个 Python script,它允许在给定项目构建日志文件的情况下按每个编译阶段打印所有单元的总摘要。它还允许按不同阶段进行排序。它还允许比较两个日志文件(例如,如果您想了解更改的影响)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-02
        • 2014-02-07
        • 1970-01-01
        • 2017-02-26
        • 2013-03-26
        • 1970-01-01
        • 2010-10-05
        相关资源
        最近更新 更多