【问题标题】:Makefile profilingMakefile 分析
【发布时间】:2011-07-21 09:25:53
【问题描述】:

所以我有这个基于 Makefile 的构建系统,我的用户觉得它工作得太慢了。为了这个问题,让我们将性能定义为弄清楚它应该实际做什么所花费的时间。

我可以看到一些优化途径 --

  • 由于包含 Makefile 片段,减少了解析 Makefile 和重新计算 DAG 的次数。
  • 减少使用make -C 转到外部 Makefile 的次数
  • 减少变量扩展

-- 但是我想先知道我的瓶颈在哪里。由于没有分析的优化是浪费生命,我想问:如何分析 Makefile?

假设我继承的系统设计得相当好,即它已经实现了行业中最常见的技巧:(主要是)非递归 make、ccache、预编译头、自动生成的头依赖等)。

... 只是为了抢占一些可能的答案。我知道可能会有比 GNU make 更快更好的构建系统——(就我个人而言,我急切地等待看到 CMake 的人会想出关于 Ninja 系统的内容)——但不幸的是,交换构建系统并不可行。

【问题讨论】:

  • 您是否知道与运行工具相比,它花费了总时间的很大一部分来决定要做什么?
  • @Mike Dunlavey:是的,运行 anything 最多需要 10 秒,开始实际工作可能需要 60 秒。
  • 有关测量整个构建性能的相关问题,而不仅仅是 Makefile 解析:stackoverflow.com/questions/6966877/…

标签: optimization profiling makefile gnu-make


【解决方案1】:

由于您对 Make 决定做什么而不是执行所需的时间感兴趣,因此您应该查看 options for getting Make to not actually do things

  • -q(问题)将让它简单地决定必须做什么,什么都不做,什么都不打印,但返回一个退出状态代码,指示是否必须做任何事情。您可以简单地为您感兴趣的任何目标(包括“全部”)计时。
  • -n(无操作)将让它打印配方,而不是执行它们。看着它们滚动会让你大致了解 Make 是如何花费时间的,如果你愿意,你可以做一些巧妙的管道技巧来计时这个过程。
  • -t (touch) 将让它接触需要重建的目标文件,而不是实际重建它们。然后,一个脚本可以查看文件的更新时间,找出最大的差距并告诉您哪些目标需要大量的深思熟虑。

编辑:

我错了。

Make 构造 DAG 并决定在重建任何目标之前必须重建哪些目标。所以一旦它开始执行规则、打印配方或接触文件,我们感兴趣的部分工作就结束了,可观察到的时间就毫无价值了。所以-n-t 选项不好,但是@987654324 @ 作为一个粗略的工具仍然很有用。还有-d会告诉你Make的思考过程;它不会告诉您时间安排,但会指出哪些目标需要考虑许多步骤。

【讨论】:

    【解决方案2】:

    在 GNU make 中添加分析已经做了一些努力。参见例如https://github.com/eddyp/make-profiler

    我最近的尝试是扩展remake 以使用valgrind callgrind format 输出信息;那么kcachegrindgprof2dot 都可以用于可视化。现在,请查看profiling branch in github。或者看到这个kcachegrind screenshot

    这一切仍在进行中,任何帮助将不胜感激。帮助可以是诸如如何更好地捕获事物(有很多信息)或如何以 callgrind 格式更好地注释它,以及改进 C 代码进行分析的内容。因此,您不一定非要成为 C 程序员才能提供帮助。

    【讨论】:

      【解决方案3】:

      我认为没有任何方法可以分析 Makefile 本身。

      你可以做一些事情:对于一个空构建(一切都是最新的),在strace -tt -fv 下运行顶级 make 并查看树的哪些部分、哪些递归子生成、哪些文件访问等等。长。

      计算变量 (var := $(shell ...))、重复 NFS 文件 stat 调用等经常使 make 变慢。

      【讨论】:

      • 特别记得使用:=而不是=,因为:=会立即展开,这样可以节省时间。
      【解决方案4】:

      这是工作,但我会获得Make 的源代码,使用调试信息构建它,然后在您等待它的时候在gdbrandomly-pause 下运行它。 这将显示它在做什么以及为什么。可能需要查看的不仅仅是调用堆栈 - 还要查看内部数据结构,因为Make 是一个解释器。 由于Make 将自己称为从属应用程序,这会使工作变得更加困难。 我必须弄清楚如何调试从属调用。

      由于它很慢,一 (1) 个样本很有可能向您展示问题。 如果您想要更多确定性,请多做几次。

      不用担心优化级别 - 瓶颈可能远不止于此。

      【讨论】:

        【解决方案5】:

        这取决于您尝试测量的内容以及您的 Makefile 的复杂程度。

        如果您只是想衡量解析,请调用一个没有依赖关系的简单目标。 但是,如果您有特定于目标的规则(例如,由于使用 $(eval)),这将无法正常工作。

        如果您还想测量推理过程(我相信这要快得多) 我没有看到调用make 的方法来实现这一点。

        如果您还想包含执行命令的 shell 的分支,事情就变得容易了:将 $(SHELL) 设置为围绕实际 shell 命令执行的时间信息。

        另一个想法是在分析器下运行make 源代码本身或添加计时消息。

        【讨论】:

          【解决方案6】:

          尝试使用-d 标志运行make。这将输出调试信息,准确概述make 正在做什么,以什么顺序,以及为什么这样做。我使用该输出来查找我会错过的东西,例如无意的递归make 调用或无序的东西,并强制对源树的某些文件或部分进行多次处理。我猜你会惊讶于make 在你不知道的幕后所做的事情。

          一个警告。 -d 选项导致make 产生大量的输出。您肯定希望将输出重定向到文件。你的终端稍后会感谢你的。

          如果您是源黑客类型,您可以构建make 的自定义版本,在由于-d 标志而打印的每一行上放置一个高分辨率时间戳。这实际上会创建一个时间线,向您准确显示make 在每个步骤上浪费了多少时间。

          【讨论】:

            【解决方案7】:

            Make 不花时间解析 Makefile,也不构建 DAG。我使用了 $(eval...) 数千行 make 的 makefile,用数百个文件构建了一个 dag,并在无事可做时几乎立即返回(好吧,在一两秒内)。

            那么你的速度在哪里?

            • 递归 make(即,在 shell 配方中启动另一个 make (${MAKE} blah...))总是很慢。 (恕我直言,这总是很糟糕。)
            • 您应该避免使用 $(shell...)——尽可能使用 make 设施
            • -jn (n > 1) 一起使用时,您的 Makefile 应该可以正常工作。确保你准确地告诉 make 什么可以与什么并行构建。尽可能不要使用 shell 循环。

            【讨论】:

            • 解析 Makefile 肯定需要时间,正如我在使用 -d 标志运行 make 命令时看到的那样。
            • 我见过很多构建,其中解析需要数秒甚至数分钟。例如,大量使用模式规则和特定于模式的变量会显着影响解析时间 (electric-cloud.com/blog/2009/04/13/…)。
            • @eric-melski:是的。有比上面提到的更多的性能占用。就我个人而言,我从不使用模式规则或模式特定变量——静态模式规则加上 $(eval) 更符合要求(并且看起来性能更高)。
            猜你喜欢
            • 2016-02-28
            • 1970-01-01
            • 2014-04-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-06-15
            • 1970-01-01
            • 2011-03-19
            相关资源
            最近更新 更多