使用内联函数有什么问题?
虽然在某些情况下使用内联函数会非常方便,
内联函数有什么缺点吗?
结论 :
显然,使用内联函数没有任何问题。
但值得注意的是以下几点!
-
内联过度使用会使程序变慢。 根据函数的大小,内联可能导致代码大小增加或减less。 内联一个非常小的访问函数通常会减less代码的大小,而内联一个非常大的函数会显着增加代码的大小。 在现代处理器上,较小的代码通常由于更好地使用指令caching而运行得更快。 – Google指南
-
内联函数的速度优势随着函数的增长而趋于减小。 在某些时候,与函数体的执行相比,函数调用的开销变得很小,并且损失了好处- Source
-
内联函数可能无法正常工作的情况很less:
- 对于返回值的函数; 如果存在返回语句。
- 对于不返回任何值的函数; 如果存在循环,开关或goto语句。
- 如果一个函数是recursion的。 -资源
-
只有在指定了optimize选项时,
__inline
inline关键字__inline
使函数内联。 如果指定了优化,是否使用__inline
取决于内联优化器选项的设置。 默认情况下,内联选项在优化器运行时生效。 如果指定了优化,则如果您希望忽略__inline
关键字,还必须指定noinline选项。 -资源
值得指出的是,inline关键字实际上只是编译器的一个提示。 编译器可以忽略内联,并简单地为某个地方的函数生成代码。
内联函数的主要缺点是可以增加可执行文件的大小 (取决于实例的数量)。 在某些平台上(如embedded式系统),这可能是一个问题,特别是如果函数本身是recursion的。
我还build议使内联函数非常小 – 内联函数的速度优势随着函数的增长而趋于减小。 在某些时候,函数调用的开销与函数体的执行相比变得很小,并且损失了好处。
这可能会增加可执行文件的大小,而且我不认为即使使用inline关键字,编译器也总是会将它们内联。 (或者是像Vaibhav所说的那样呢?…)
我认为如果函数只有1或2个语句通常是可以的。
编辑:这是什么linux CodingStyle文件说:
第15章:在线疾病
似乎有一个常见的误解,gcc有一个神奇的“让我更快”加速选项称为“内联”。 虽然使用内联可能是合适的(例如,作为replacemacros的一种手段,请参阅第12章),但通常情况并非如此。 大量使用inline关键字会导致更大的内核,从而导致整个系统的速度变慢,这是由于CPU的icache占用空间更大,并且因为页面caching的可用内存更less。 考虑一下; 一个页面caching缺失导致磁盘寻道,这很容易花费5毫秒。 有很多cpu周期可以进入这5毫秒。
一个合理的经验法则是不要把内联放在超过3行代码的函数中。 这个规则的一个例外是一个参数被认为是一个编译时常量的情况,由于这个常量,你知道编译器能够在编译时将大部分函数优化掉。 有关后面这种情况的一个很好的例子,请参阅kmalloc()内联函数。
通常人们认为,将函数内联添加到静态函数并且只使用一次总是赢,因为没有空间折衷。 虽然这在技术上是正确的,但gcc能够自动内联这些内容,而当第二个用户出现时,内联的维护问题超过了提示的潜在价值,告诉gcc做一些本来可以做的事情。
我同意其他post:
- 内联可能是多余的,因为编译器会这样做
- 内联可能会膨胀你的代码
第三点是它可能会迫使你在头文件中公开实现细节,例如.eg,
class OtherObject; class Object { public: void someFunc(OtherObject& otherObj) { otherObj.doIt(); // Yikes requires OtherObj declaration! } };
如果没有内联,则只需要使用OtherObject的前向声明。 用inline你的头文件需要定义OtherObject。
正如其他人所提到的,inline关键字只是编译器的一个提示。 实际上,大多数现代编译器都会完全忽略这个提示。 编译器有自己的启发式来决定是否内联一个函数,而且坦率地说不需要你的build议,非常感谢。
如果你真的想真正实现内联,如果你已经对它进行了分析,并且看到反汇编,以确保覆盖编译器的启发式实际上是有意义的,那么有可能:
- 在VC ++中,使用__forceinline关键字
- 在GCC中,使用__attribute __((always_inline))
inline关键字确实有第二个有效的用途 – 在头文件中声明函数,但不在类定义中声明。 需要inline关键字来告诉编译器不要生成函数的多个定义。
内联有一个问题 – 一旦你在头文件中定义了一个函数(这意味着内联,通过在类内部定义一个成员函数的主体来显式或隐式),没有简单的方法来改变它,而不会强迫你的用户重新编译(而不是重新链接)。 通常这会导致问题,特别是如果有问题的函数是在库中定义的,并且头是其接口的一部分的话。
我对此表示怀疑。 甚至编译器也会自动将一些函数内联到优化中。
我不知道我的答案是否与这个问题有关,但是:
内联虚拟方法要非常小心! 一些漏洞百出的编译器(例如以前版本的Visual C ++)会为虚拟方法生成内联代码,其中标准行为是什么都不做,只能inheritance树并调用适当的方法。
内联较大的函数可能会使程序变大,导致更多的caching未命中并使其变慢。
决定一个函数足够小,内联会提高性能是相当棘手的。 Google的C ++风格指南build议只内联10行或更less的函数。
您还应该注意,inline关键字只是一个请求。 编译器可以select不内联它,同样,如果编译器认为速度/大小权衡是值得的,那么编译器可能会select将内联函数作为内联函数。
这个决定是基于一些东西,比如优化速度(避免函数调用)和优化大小(内联会导致代码膨胀,所以对于大的重复使用的函数不太好)之间的设置。
使用VC ++编译器,您可以使用__forceinline
覆盖此决定
一般来说:如果你真的想在一个头文件中有一个函数,那么使用内联,但是在别的地方,因为如果你想从中获得任何东西,一个好的编译器将会为你内联。
过多的函数内联可能会增加编译的可执行文件的大小,这会对caching性能产生负面影响,但是现在编译器自己决定函数内联(取决于许多标准)并忽略内联关键字。
在内联函数的其他问题中,我已经看到了过度使用(我已经看到了500行的内联函数),您必须注意的是:
-
造成不稳定
- 更改内联函数的来源会导致标题的所有用户重新编译
-
#include
泄漏到客户端。 这可能是非常讨厌的,如果你修改一个内联函数,并删除一些客户端依赖的不再使用的头。
-
可执行大小
- 每次内联内联而不是调用指令时,编译器必须生成内联的整个代码。 如果函数的代码很短(一行或两行),那么这是可以的,如果函数很长,则不太好
- 有些函数比第一次出现代码要多得多。 我的例子是一个类的“微不足道”的析构函数,它有很多非pod成员variables(或者两个或三个成员variables,而且相当凌乱的析构函数)。 必须为每个析构函数生成一个调用。
-
执行时间处理时间
- 这是非常依赖于你的CPUcaching和共享库,但参考的地方是重要的。 如果你可能内联的代码恰好在一个地方被保存在cpucaching中,一些客户端可以发现代码不会受到caching缺失和随后的内存获取(更糟糕的是,如果发生这种情况,则是磁盘获取) 。 可悲的是,这是你真的需要做性能分析的情况之一。
在我工作的编码标准中,内联函数限制了简单的设置者/获取者,具体而言,parsing器不应该是内联的,除非你有性能测量来显示内联赋予一个明显的优势。
-
正如其他人所说,如果代码很大,内联函数会产生一个问题。由于每条指令都存储在一个特定的内存位置,所以内联函数的重载使得代码需要更多的时间来获取结果。
-
内联可能无法工作的其他情况很less
- 在recursion函数的情况下不起作用。
- 它也可能不适用于静态variables。
- 它也不工作,如果有使用循环,开关等情况下,我们可以说,与多个报表。
- 而函数main不能作为内联函数。