【问题标题】:If a function is never called does the compiler omit it when creating the program?如果一个函数从未被调用,编译器会在创建程序时忽略它吗?
【发布时间】:2013-04-09 00:30:07
【问题描述】:

当编译器运行其优化时,它是否会忽略从未调用过的函数?我记得读过一些说不要包含不必要的文件的东西,因为它会在程序中造成不必要的膨胀,所以我只能从该声明中假设,当它被编译时,它确实包含了从未使用过的函数,但我没有看到任何合乎逻辑的编译器不删除它们的原因,我认为大多数现代编译器可能会删除额外未使用的函数。

【问题讨论】:

  • 如果发生这种情况,我认为不会存在公共功能。我可能是错的,因为我已经多年没有学习编译器理论了。
  • 从未调用过,或从未引用过?一般来说,编译器不可能确定一个函数是否会被调用。

标签: c++ performance optimization


【解决方案1】:

一般来说,答案是“不”。但是一些编译器会在某些特定情况下消除一些功能。并且链接器也不会包含来自源文件中不需要该文件中的函数的静态链接库中的函数。

一个通常被消除的常见情况是从不调用静态函数并且从不获取谁的地址。另一个是静态函数,它在被调用的任何地方都内联,并且永远不会占用谁的地址。

这完全取决于编译器。

但是,一般来说,这很难做到,因为编译器通常没有整个程序的视图来构建一个全面的调用图。而且链接器不够智能。

此外,某些文件可能最终成为库的一部分。库的全部目的是拥有一堆不从库本身调用的代码。编译器无法在编译时确定给定的代码段是否进入库。再说一遍,这意味着对于具有“外部链接”的函数,如果不使用它们,则由链接器来忽略它们。

【讨论】:

  • the compiler doesn't usually have a view of the whole program to build a comprehensive call graph. And the linker isn't smart enough. 现在 LTO 已广泛使用,编译器和链接器可以如此紧密地通信,以至于它们实际上可以成为一个单元,但这些陈述都不正确。例如,GCC 的 -flto 谈到了它如何将所有翻译单元一起流式传输到 1 中,为此构建了一个完整的程序调用图,从而允许进行强烈的优化。生成的堆栈跟踪(或未定义的引用!)显示了令人难以置信的内联水平和正在进行的其他优化
  • @underscore_d - 编译器是否也使用它来改善模板实例化的情况?整个“我将把这个函数定义作为一个弱符号发出,并祈祷所有其他编译单元为这个符号发出相同的代码,这样当链接器随机选择一个时,它就会正确。”在我看来真的很狡猾。
  • 好问题——我都不知道;我没有仔细看那个案子。无论如何,弱符号对我来说仍然很大程度上是巫术。当然,尽管如此,他们都必须表现出相同的行为,所以选择哪个并不重要?或者如果模板参数不同,则实例化的函数不是合并的候选者,所以没关系。但我可能误解了大部分内容。 :P
【解决方案2】:

这在很大程度上取决于编译器,但是是的,在某些设置中,编译器可以完全消除未调用的函数。编译器必须确保该函数确实从未被调用。

标记为static 的特定功能可以很容易地消除 - 只需查看同一个翻译单元即可。如果一个函数可能被另一个翻译单元调用,那就更难了。 Visual C++ 对此有一个特殊设置,称为“函数级链接”和链接器设置for removing unreferenced functions。当这两个一起使用时,未引用的函数将被完全消除。

【讨论】:

    【解决方案3】:

    可以做到。 C++ 编译器仅在 as-if 规则下运行。只要生成的程序按照标准要求运行,编译器就可以为所欲为。

    需要符合要求的实现来模拟(仅)抽象机器的可观察行为

    如果该函数从未被调用,那么编译器当然可以摆脱它,但它是否完全取决于编译器。但是,C++ 的编译模型需要单独编译翻译单元。编译器通常不可能或很难知道该函数从未从任何其他翻译单元调用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多