38

虽然在某些情况下使用内联函数会非常方便,

内联函数有什么缺点吗?

结论

显然,使用内联函数并没有错。

但值得注意的是以下几点!

  • 过度使用内联实际上会使程序变慢。根据函数的大小,内联它可能会导致代码大小增加或减少。内联非常小的访问器函数通常会减少代码大小,而内联非常大的函数会显着增加代码大小。在现代处理器上,由于更好地使用指令缓存,较小的代码通常运行得更快。- 谷歌指南

  • 随着函数大小的增长,内联函数的速度优势趋于减弱。在某些时候,函数调用的开销与函数体的执行相比变得很小,并且失去了好处- 来源

  • 在少数情况下内联函数可能不起作用:

    • 对于返回值的函数;如果存在 return 语句。
    • 对于不返回任何值的函数;如果存在循环、switch 或 goto 语句。
    • 如果函数是递归的。-来源
  • 仅当您指定优化选项时,__inline关键字才会内联函数。如果指定了优化,是否__inline兑现取决于内联优化器选项的设置。默认情况下,只要运行优化器,内联选项就会生效。如果您指定 optimize ,如果您希望__inline关键字被忽略,则还必须指定 noinline 选项。-来源

4

13 回答 13

21

值得指出的是,inline 关键字实际上只是给编译器的一个提示。编译器可能会忽略内联,而只是在某个地方为函数生成代码。

内联函数的主要缺点是它会增加可执行文件的大小(取决于实例化的数量)。这在某些平台(例如嵌入式系统)上可能是一个问题,特别是如果函数本身是递归的。

我还建议使内联函数非常小- 内联函数的速度优势往往会随着函数大小的增长而减少。在某些时候,函数调用的开销与函数体的执行相比变得很小,并且失去了好处。

于 2008-09-13T20:38:49.460 回答
14

它可能会增加可执行文件的大小,而且我认为即使您使用了 inline 关键字,编译器也不会总是将它们内联。(或者相反,就像Vaibhav 所说的那样?...)

我认为如果函数只有 1 或 2 条语句通常是可以的。

编辑:这是 linux CodingStyle文档所说的:

第15章:内联疾病

似乎有一个普遍的误解,即 gcc 有一个神奇的“让我更快”加速选项,称为“内联”。虽然使用内联可能是合适的(例如,作为一种替换宏的方法,参见第 12 章),但通常情况并非如此。大量使用 inline 关键字会导致更大的内核,这反过来又会降低整个系统的速度,这是因为 CPU 占用的 icache 占用空间更大,并且仅仅是因为页面缓存可用的内存更少。考虑一下; pagecache 未命中会导致磁盘查找,这很容易花费 5 毫秒。有很多 cpu 周期可以进入这 5 毫秒。

一个合理的经验法则是不要将 inline 放在其中包含超过 3 行代码的函数中。此规则的一个例外是参数已知为编译时常量的情况,并且由于这种常量性,您知道编译器将能够在编译时优化您的大部分函数。有关后一种情况的一个很好的示例,请参见 kmalloc() 内联函数。

人们经常争辩说,将内联添加到静态且只使用一次的函数中总是一种胜利,因为没有空间权衡。虽然这在技术上是正确的,但 gcc 能够在没有帮助的情况下自动内联这些,并且当第二个用户出现时删除内联的维护问题超过了告诉 gcc 做它本来会做的事情的提示的潜在价值。

于 2008-09-13T20:28:46.020 回答
5

内联存在问题 - 一旦您在头文件中定义了一个函数(这意味着内联,通过在类中定义成员函数的主体显式或隐式),没有简单的方法可以在不强制用户重新编译的情况下更改它(而不是重新链接)。这通常会导致问题,特别是如果所讨论的函数是在库中定义的,并且标头是其接口的一部分。

于 2008-09-15T22:20:03.083 回答
4

我同意其他帖子:

  • inline 可能是多余的,因为编译器会这样做
  • 内联可能会使您的代码膨胀

第三点是它可能会迫使您在标头中公开实现细节,例如,

class OtherObject;

class Object {
public:
    void someFunc(OtherObject& otherObj) {
        otherObj.doIt(); // Yikes requires OtherObj declaration!
    }
};

如果没有内联,您只需要OtherObject 的前向声明。使用内联,您的标题需要 OtherObject 的定义。

于 2008-09-13T22:28:06.857 回答
4

正如其他人所提到的, inline 关键字只是对编译器的提示。事实上,大多数现代编译器都会完全忽略这个提示。编译器有自己的启发式方法来决定是否内联函数,坦率地说不想要你的建议,非常感谢。

如果您真的非常想内联,如果您实际上已经对其进行了分析并查看了反汇编以确保覆盖编译器启发式实际上是有意义的,那么它是可能的:

  • 在 VC++ 中,使用 __forceinline 关键字
  • 在 GCC 中,使用 __attribute__((always_inline))

然而, inline 关键字确实有第二个有效的目的——在头文件中声明函数,而不是在类定义中。需要 inline 关键字来告诉编译器不要生成函数的多个定义。

于 2008-09-14T12:26:59.947 回答
2

我对此表示怀疑。甚至编译器也会自动内联一些函数进行优化。

于 2008-09-13T20:26:12.250 回答
2

我不知道我的回答是否与问题有关,但是:

小心内联方法!一些有缺陷的编译器(例如以前的 Visual C++ 版本)会为虚拟方法生成内联代码,其中标准行为是什么都不做,只是沿着继承树向下并调用适当的方法。

于 2008-09-16T07:35:02.500 回答
1

您还应该注意 inline 关键字只是一个请求。编译器可能会选择不内联它,同样,如果编译器认为速度/大小权衡是值得的,它可能会选择将您未定义为内联的函数内联。

这个决定通常是基于许多事情做出的,例如优化速度(避免函数调用)和优化大小之间的设置(内联会导致代码膨胀,因此对于大型重复使用的函数来说不是很好)。

使用 VC++ 编译器,您可以使用__forceinline

所以一般来说:如果你真的想在头文件中有一个函数,请使用内联,但在其他地方没有什么意义,因为如果你想从中获得任何东西,一个好的编译器无论如何都会为你内联。

于 2008-09-13T20:41:07.817 回答
1

内联较大的函数会使程序变大,从而导致更多的指令缓存未命中并使其变慢。

确定一个函数何时足够小以至于内联会提高性能是非常棘手的。Google 的 C++ Style Guide建议仅使用 10 行或更少的内联函数。

(简化)示例

想象一个简单的程序,它只调用函数“X”5 次。

如果 X 很小并且所有调用都内联:可能所有指令都将通过单个主存储器访问预取到指令缓存中 - 太棒了!

如果 X 很大,假设接近指令缓存的容量:
内联 X 可能会导致 X 的每个内联实例从内存中获取一次指令。
如果 X 未内联,则可能会在第一次调用时从内存中获取指令到 X,但可能会保留在缓存中以供后续调用。

于 2008-09-14T11:58:54.147 回答
0

函数的过度内联会增加编译后的可执行文件的大小,这会对缓存性能产生负面影响,但现在编译器会自行决定函数内联(取决于许多标准)并忽略 inline 关键字。

于 2008-09-13T20:31:27.403 回答
0

在我已经看到过度使用的内联函数的其他问题中(我已经看到了 500 行的内联函数),您必须注意的是:

  • 建立不稳定

    • 更改内联函数的源代码会导致头文件的所有用户重新编译
    • #includes 泄漏到客户端。如果您重新编写内联函数并删除某些客户端所依赖的不再使用的标头,这可能会非常令人讨厌。
  • 可执行文件大小

    • 每次内联而不是调用指令时,编译器都必须生成内联的整个代码。如果函数的代码很短(一两行),这是可以的,如果函数很长,则不太好
    • 一些函数可以产生比最初出现的更多的代码。我的例子是一个类的“微不足道”的析构函数,它有很多非 pod 成员变量(或两个或 3 个具有相当混乱的析构函数的成员变量)。必须为每个析构函数生成一个调用。
  • 执行时间处理时间

    • 这非常依赖于您的 CPU 缓存和共享库,但引用的位置很重要。如果您可能要内联的代码恰好保存在 cpu 缓存中的某个位置,那么许多客户端可以发现该代码不会遭受缓存未命中和随后的内存提取(更糟糕的是,如果发生这种情况,磁盘提取) . 可悲的是,这是您真正需要进行性能分析的情况之一。

我工作的编码标准将内联函数限制为简单的 setter/getter,特别是析构函数不应该是内联的,除非你有性能测量来显示内联具有明显的优势。

于 2016-07-25T08:34:55.760 回答
0

除了其他很好的答案之外,至少有一次我看到强制内联实际上将受影响的代码减慢了 1.5 倍的情况。里面有一个嵌套循环(非常小),当这个函数被编译为一个单独的单元时,编译器设法有效地展开和优化它。但是当同一个函数被内联到更大的外部函数中时,编译器(MSVC 2017)未能优化这个循环。

于 2021-11-16T14:28:08.760 回答
-1
  1. 正如其他人所说,如果代码很大,内联函数会产生问题。由于每条指令都存储在特定的内存位置,因此内联函数的重载会使代码花费更多时间来执行。

  2. 在其他情况下内联可能不起作用

    1. 在递归函数的情况下不起作用。
    2. 它也可能不适用于静态变量。
    3. 如果使用循环、开关等,它也不起作用。或者我们可以用多个语句说。
    4. 并且函数 main 不能用作内联函数。
于 2016-07-24T17:14:35.527 回答