【问题标题】:Can I selectively (force) inline a function?我可以选择性地(强制)内联函数吗?
【发布时间】:2011-08-18 14:12:03
【问题描述】:

在书Clean Code(以及我遇到并阅读的其他几本)中,建议保持函数很小,如果它们变大则将它们分解。它还建议函数应该只做一件事且只做一件事。

Optimizing software in C++Agner Fog 中表示,他不喜欢仅仅因为函数越过一定数量的行数就将其分解的规则。他指出,这会导致不必要的跳转,从而降低性能。

首先,我明白,如果我正在处理的代码不是处于紧密循环中并且函数很繁重,因此调用它们所花费的时间与代码中的时间相比相形见绌。函数需要执行。但是让我们假设我正在使用大多数时候被其他对象/函数使用并且正在执行相对微不足道的任务的函数。这些功能遵循第一段中列出的建议(即执行一个单一功能并且很小/易于理解)。然后我开始编写一个性能关键函数,它在一个紧密的循环中利用这些其他函数,本质上是一个框架函数。最后,假设内联它们对性能关键功能有好处,但对任何其他功能没有任何好处(是的,我已经对此进行了分析,尽管我想避免大量的复制和粘贴)。

马上,可以说标记函数inline 并让编译器选择。但是,如果我不希望所有这些函数都在 `.inl 文件中或在标头中公开,该怎么办?在我目前的情况下,性能关键函数和它使用的其他函数都在同一个源文件中。

总而言之,我是否可以选择性地(强制)内联单个函数的函数,以便最终代码表现得像一个大函数,而不是多次调用其他函数。

【问题讨论】:

  • 除非你使用预处理器,这根本不是一回事,否则编译器可以忽略你所做的任何表明函数应该被内联的事情。
  • gcc 提供了 -Winline 选项来警告您有关已标记为 inline 但未标记的函数。这是一个开始,但不能解决您的问题。
  • 由于这些函数都在正确的源文件中,并且没有在其他地方使用,我想说只需标记它们static inline 并让编译器继续它。如果它认为不内联它们会产生更好的代码,谁知道呢,也许是对的。如果你指定一个编译器,人们可以建议它必须强制内联的任何选项,但由于你似乎想要一个可移植的解决方案,它是“让编译器优化,它比你更了解平台,因为你知道与平台无关”。
  • 这个线程很旧,但我刚刚偶然发现。我处于一种情况,我的代码很糟糕,为了能够理解类和较小的函数,我将其分解。虽然它会产生开销,但如果有一个瓶子,我更有可能优化好的书面代码,而不是我能够修复“快速”代码。
  • 我认为应该有办法做到这一点。我创建了一个 STL 算法和编译器 GCC 的函数版本,并且 clang,不要内联函数,它比 STL 花费 10 倍以上的时间,当我将函数粘贴到里面时,它只需要 0.9 倍。是的!有时需要强制内联。

标签: c++ optimization coding-style inline


【解决方案1】:

没有什么可以阻止您将 inline 放入 .cpp 文件中的静态函数中。

一些编译器可以选择强制内联函数,参见例如GCC 属性((always_inline)) 和大量用于微调内联优化的选项(请参阅 -minline-* 参数)。

我的建议是在你认为合适的地方使用 inline 甚至更好的 static inline,让编译器决定。他们通常做得很好。

【讨论】:

  • 这是个好建议。它在我的特定情况下不起作用,但绝对要牢记(+1)。
  • 在一些(主要是嵌入式)项目中,经常出现代码的逻辑分离但“物理”连续性的需求。例如,考虑中断处理程序例程,其中必须在开始和结束时都执行许多强制性操作。这些操作可以完美地实现为静态内联函数,只要保证中断例程不会被额外的堆栈陶器和调用指令污染(即函数确实是内联的)。
【解决方案2】:

您不能强制内联。此外,与完成工作的成本相比,现代 CPU 上的函数调用非常便宜。如果您的函数足够大以至于需要分解,那么执行调用所花费的额外时间基本上是什么。

如果做不到这一点,您可以...尝试...使用宏。

【讨论】:

  • 我改变了我的风格,以匹配各种书籍作者的建议(我肯定比我更了解)编写函数,以便他们做他们名字所暗示的,仅此而已,并保持函数垂直长度小。这导致许多较小的函数对我最近的性能敏感代码产生了性能影响。读完 Agner Fog 的书后,我开始思考另一方面,想知道是否应该在两种截然不同的建议之间取得平衡(选择性内联本来是理想的解决方案 - 可能是宏)
  • 现在可以使用__forceinline (msdn.microsoft.com/en-us/bw1hbe6y)
  • _forceinline 指示编译器尽最大努力内联函数而不执行任何成本/收益分析。 docs.microsoft.com/en-us/cpp/cpp/…
  • 这已经过时了,请参阅下面的其他一些答案。
【解决方案3】:

不,inline 是对编译器的建议;它不会强迫它做任何事情。此外,如果您使用的是 MSVC++,请注意 __forceinline 也是用词不当;这只是比inline 更强的推荐。

【讨论】:

  • 我明白了。编译器无论如何都不能内联一些函数,但是当使用__forceinline时它不会考虑自己的分析,如果可以的话,函数会被内联。
  • @Samaursa:是的,但重点是您永远不能假设该函数将内联inline 关键字的任何变体(根据您的问题)。最多,您可能能够增加它被内联的机会。
  • @Jacob:你不能假设它,但你可以通过使用/Wall 启用所有警告并注意哪些函数没有内联来验证它。
  • 这里有大量信息:docs.microsoft.com/en-us/cpp/cpp/…
【解决方案4】:

这与老式的纯 C 和 C++ 一样多。前几天我在思考这个问题,因为在嵌入式世界中,速度和空间都需要仔细管理,这真的很重要(而不是经常“不用担心,你的编译器很聪明,而且内存在桌面/服务器开发中很普遍)。

我尚未审查的可能解决方案基本上是为不同的变体使用两个名称,例如

inline int _max(int a, int b) {
    return a > b ? a : b;
}

然后

int max(int a, int b) {
    return _max(a, b);
}

这将使人们能够有选择地调用 _max() 或 max() 并且仍然只定义一次算法。

【讨论】:

    【解决方案5】:

    内联 - 例如,如果存在一个函数 A 经常调用函数 B,而函数 B 相对较小,那么 profile-guided optimizations 会将函数 B 内联到函数 A 中。

    VS Profile-Guided Optimizations

    您可以使用性能和诊断中心中的 Visual C++ 自动配置文件引导优化插件来简化和简化 Visual Studio 中的优化过程,也可以在 Visual Studio 中或在命令行中手动执行优化步骤. 我们推荐该插件,因为它更易于使用。有关如何获取该插件并使用它来优化您的应用程序的信息,请参阅Profile Guided Optimization Plug-In

    【讨论】:

      【解决方案6】:

      如果你有一个已知的热门函数,并且希望编译器比平常更积极地内联,那么 gcc/clang 提供的 flatten 属性可能是值得研究的。与 inline 关键字和属性相反,它适用于关于在标记函数中调用的函数的内联决策。

      __attribute__((flatten)) void hot_code() {
          // functions called here will be inlined if possible
      }
      

      有关官方文档,请参阅 https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.htmlhttps://clang.llvm.org/docs/AttributeReference.html#flatten

      【讨论】:

        【解决方案7】:

        编译器实际上非常擅长生成优化代码。

        我建议将您的代码组织成逻辑分组(如果可以提高可读性,则使用其他函数),如果合适,将它们标记为内联,并让编译器决定以最佳方式生成哪些代码。

        【讨论】:

        • 在某些情况下不适用:有时,inline 被用作宏的替代品,因为它们是可调试的、更具可读性并且可以放入命名空间中。有时,堆栈帧分配可能会导致宝贵的微秒延迟,并且编译器可能不知道没有上下文的执行频率(如果您内联 sin() 函数并且编译器由于某种原因没有内联它怎么办?)。跨度>
        【解决方案8】:

        很惊讶这还没有被提及,但到目前为止,您可以告诉编译器(我相信它可能仅适用于 GCC/G++)强制内联函数并忽略与其相关的一些限制。

        您可以通过__attribute__((always_inline))这样做。

        使用示例:

        inline __attribute__((always_inline)) int pleaseInlineThis() {
           return 5;
        }
        

        通常你应该避免强制内联,因为编译器知道什么比你更好;但是有几个用例,例如在 OS/MicroController 开发中,您需要内联调用,如果调用它,则会破坏功能。

        C++ 编译器通常对受控环境不太友好,例如没有一些 hack 的环境。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-20
          • 1970-01-01
          • 2013-09-12
          • 1970-01-01
          • 2014-05-08
          • 2012-12-12
          • 1970-01-01
          • 2010-09-16
          相关资源
          最近更新 更多