【问题标题】:Is the GNU C++ compiler ((GCC) 5.2.0) good at "inlining" by itself?GNU C++ 编译器((GCC)5.2.0)本身是否擅长“内联”?
【发布时间】:2015-08-30 17:13:13
【问题描述】:

我不是在问为什么或何时应该在某些代码中使用inline

我使用inline,我认为这是很常见的(比如在get/set函数中)。我在堆栈上阅读了一些不寻常的示例,其中inline 听起来很有必要。我不确定我是否会在我的代码中识别出这种情况...我的意思是,直到我遇到问题...

所以我想知道对于通常情况,编译器是否能像我一样处理这个问题,甚至可能更好......

我想避免inline的原因是为了避免在标题中实现......关于这个问题我脑海中浮现:

编译器(在我的问题中总是考虑 GCC 编译器)是否能够inline 标头中未实现的函数?

n.b. : 不知道C++14中inline是否有一些内部变化。

【问题讨论】:

  • 如果你在头文件中实现了一个非内联函数,当从多个源文件中包含多重定义的符号时,你会得到一个链接器错误
  • @NikosAthanasiou 好吧,我不想在标题中实现......正如我在问题中写的那样
  • @juanchopanza,“是/是否......?”我打赌,我输了……
  • 您说的是“内联”方法,即在类定义中实现?
  • gcc 不能从另一个编译单元内联函数,除非您使用链接时间优化(通过 -flto 标志进行编译和链接) - 但在这种情况下,它做得很好。跨度>

标签: c++ gcc g++ c++14


【解决方案1】:

首先内联一些东西,即在调用点需要内联替换函数体并不意味着实现被限制这样做

7.1.2 函数说明符

2 带有 inline 说明符的函数声明(8.3.5、9.3、11.3)声明了一个内联函数。 inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。 在调用点执行此内联替换不需要实现;但是,即使省略了这个内联替换,7.1.2 中定义的内联函数的其他规则仍应得到遵守。

仅此一项就应该让人感觉到,在这个主题上依赖编译器优化(或者这样做很重要)是一种模糊的启发式方法。例如,我们知道编译器很难内联函数指针调用,但这并不重要。

有一些标准要求内联的语言结构示例,这不是成为一个好的编译器而是一个兼容的编译器的问题。

闭包类型的函数调用运算符

5.1.2

5 非泛型 lambda 表达式的闭包类型具有公共内联函数调用运算符 (...) 对于泛型 lambda,闭包类型具有公共内联函数调用运算符成员模板 (...)

constexpr 函数

7.1.5

2 (...) constexpr 函数和 constexpr 构造函数是隐式的 内联 (7.1.2)。

删除的函数

8.4.3

已删除的函数是隐式内联的。 [ 注意:单一定义规则 (3.2) 适用于已删除的定义。 — 尾注] 删除的函数定义应是函数的第一个声明,或者,对于显式 函数模板的特化,该特化的第一个声明。

其他示例是在类定义中定义的成员函数和在类的友元声明中定义的函数。内联并不是每种情况下的最佳选择(可能会增加程序大小),因此对于性能关键部分,我建议您进行基准测试并自行决定;毕竟Do inline functions improve performance?

【讨论】:

  • 感谢您的回答!实际上我从未使用过constexpr 函数,而且我还不知道什么是“已删除函数”。另外我必须仔细阅读您关于“内联函数是否提高性能?”的文章。你值得 +1!
【解决方案2】:

如果没有某种示例代码,很难确定编译器会做什么(即使有代码,也可能非常困难)。

一般来说,如果函数很小,编译器将内联在编译期间有源代码可用的函数(如果您启用 lto(链接时间优化),则编译器将内联其他函数。如果您希望能够将函数编译成目标文件并内联函数,然后你必须使用 lto。

在大多数情况下,只调用一次的大型静态函数也将被内联。

当您有多个调用并且函数“大小不一”时,问题就出现了 - 换句话说,长度超过 3-4 行。然后编译器必须在内联和膨胀代码之间平衡,而不是内联和调用成本。这是一个很难做到的平衡。配置文件驱动的优化在这里可以提供很大帮助,但一般来说,编译器将大部分正确,但完全不能保证 - 如果有疑问(并且这很重要),请使用应用程序的基准,并衡量它是否变得更好具有强制内联属性。

【讨论】:

  • 我没有添加代码,因为正如您所说,“即使有代码,它也可能非常困难”。和(也许我认为这很明显是错误的)但是 get/set 函数不会占用更多的一行。到目前为止我从未使用过lto 标志,我会在未来考虑它。谢谢你的回答
  • 通常,对于 gettter/setter 函数,我将它们内联到类声明中。这样,它们就可供编译器内联。
  • 我也使用内联它们,但有时你必须处理前向声明......依赖......我尽量让我的标题更轻。
  • 当然,正如我所说,如果性能非常重要,请进行基准测试、分析并检查代码实际花费的时间。大部分函数只被调用几次,其实没多大关系……
  • 我不太清楚如何进行良好的基准测试。这是我必须在“学习”列表中添加的内容。
【解决方案3】:

编译器可以内联在同一文件中使用的函数(技术上是“编译单元”),但不能在编译不同文件时内联,因为它没有源代码。这就是为什么内联函数应该放在头文件中的原因。

【讨论】:

  • “但不是在编译不同的文件时” - 不正确,请参阅-flto
猜你喜欢
  • 2011-08-05
  • 1970-01-01
  • 2013-05-19
  • 2019-05-11
  • 1970-01-01
  • 1970-01-01
  • 2012-06-25
  • 2016-12-09
  • 2020-12-14
相关资源
最近更新 更多