使用内联函数有什么问题?

时间:2008-09-13 20:25:07

标签: c++ inline-functions

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

内联函数有任何缺点吗?

结论

显然,使用内联函数没有任何问题。

但值得注意以下几点!

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

  • 随着函数大小的增加,内联函数的速度优势会逐渐减弱。在某些时候,与函数体的执行相比,函数调用的开销变小,并且失去了好处- Source

  • 内联函数可能无法正常工作的情况很少:

    • 对于返回值的函数;如果存在return语句。
    • 对于不返回任何值的函数;如果存在循环,开关或goto语句。
    • 如果函数是递归的。 -Source
  • 只有在指定optimize选项时,__inline关键字才会导致函数内联。如果指定了optimize,则__inline是否受到尊重取决于内联优化器选项的设置。默认情况下,只要运行优化程序,内联选项就会生效。如果指定optimize,则还必须指定noinline选项(如果要忽略__inline关键字。 -Source

12 个答案:

答案 0 :(得分:21)

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

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

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

答案 1 :(得分:14)

它可能会增加。的大小  可执行,我不认为  编译器总是会实际制作  即使你使用了它们,它们也是内联的  内联关键字。 (或者是另一个  方式,像Vaibhav  所述?...)

我认为通常情况下是好的  函数只有1或2个语句。

编辑:这是linux CodingStyle文档中有关它的内容:

  

第15章:内联疾病

     

似乎有一个共同点   误以为gcc有魔力   “让我更快”称为加速选项   “排队”。虽然使用内联可以   适当的(例如作为一种手段   替换宏,见第12章),   它经常不是。大量使用   内联关键字带来了很多   更大的内核,反过来减慢了   系统整体下来,由于一个   CPU的更大icache足迹   而且仅仅因为少了   内存可用于pagecache。   考虑一下;一个pagecache miss   导致磁盘搜索,这很容易   5毫秒。有很多cpu   可以进入这5个循环   毫秒。

     

合理的经验法则是不   将内联函数放在更多的函数中   超过3行代码。一个   这个规则的例外是案例   其中参数已知为a   编译时常量,结果   你知道的这种恒定性   编译器将能够优化最多   在编译时你的功能。   对于后一种情况的一个很好的例子,   请参阅kmalloc()内联函数。

     

通常人们认为添加内联   到静态和使用的函数   从那以后,只有一次总是一场胜利   没有空间权衡。虽然这是   技术上正确,gcc是有能力的   无需自动内联这些内容   帮助和维护问题   在第二个用户时删除内联   似乎超过潜在价值   告诉gcc要做的提示   无论如何它本来会做的事情。

答案 2 :(得分:4)

我同意其他帖子:

  • 内联可能是多余的,因为编译器会这样做
  • 内联可能会破坏您的代码

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

class OtherObject;

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

如果没有内联,则只需要一个OtherObject的前向声明即可。内联你的 header需要OtherObject的定义。

答案 3 :(得分:4)

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

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

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

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

答案 4 :(得分:4)

内联存在问题 - 一旦你在头文件中定义了一个函数(暗示内联,通过在类中定义成员函数的主体,显式或隐式),没有简单的方法来改变它而不强迫你用户重新编译(而不是重新编译)。这通常会导致问题,特别是如果有问题的函数在库中定义并且标题是其界面的一部分。

答案 5 :(得分:2)

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

答案 6 :(得分:2)

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

非常小心内联虚拟方法!一些错误的编译器(例如Visual C ++的早期版本)将为虚拟方法生成内联代码,其中标准行为除了继承树并调用适当的方法之外什么都不做。

答案 7 :(得分:1)

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

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

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

覆盖此决策

一般来说: 如果你真的想在标题中使用函数,请使用内联,但在其他地方也没有什么意义,因为如果你想从中获得任何东西,那么一个好的编译器会让你为它内联。

答案 8 :(得分:1)

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

确定函数何时足够小,内联将提高性能是非常棘手的。 Google's C++ Style Guide建议仅内联10行或更少的函数。

答案 9 :(得分:0)

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

答案 10 :(得分:0)

内联函数的其他问题,我已经看到过度使用(我已经看到500行的内联函数),你需要注意的是:

  • 构建不稳定性

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

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

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

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

答案 11 :(得分:-1)

  1. 正如其他人所说,如果代码很大,内联函数可能会产生问题。由于每条指令都存储在特定的内存位置,因此内联函数的重载会使代码花费更多时间才能获得排名

  2. 其他几种内联可能无法正常工作的情况

    1. 在递归函数的情况下不起作用。
    2. 它也可能不适用于静态变量。
    3. 如果使用循环,切换等,它也不起作用。或者我们可以用多个语句来说。
    4. 并且函数main不能用作内联函数。