__inline函数与C中的普通函数

时间:2012-04-12 19:26:43

标签: c inline c99

我正在研究宏,并发现了许多关于宏和内联函数之间差异的来源和问题。但没有什么具体指定和区分内联与正常功能的优缺点。

但是,如果我想在正常功能和内联功能之间进行选择呢?

我知道使用内联函数会增加代码大小。但是,虽然研究规模不是主要问题,但效率是目标。使函数成为内联函数表明对函数的调用尽可能快。(由于堆栈和填充开销)

总是使用内联函数是否更好?如果没有,那么为什么?使用普通函数比内联有什么好处?

在阅读其他问题时,我读到内联只是对编译器的一个暗示。编译器可能会忽略它。编译器何时忽略它并为什么

5 个答案:

答案 0 :(得分:8)

内联函数可以有几个优点:

  1. 它可以使程序大小更小。当函数仅使用一次时通常就是这种情况。另请参阅2.和3.

  2. 如果编译器知道变量是常量,或非NULL,或类似的东西,编译器可以消除函数的未使用位。这可以节省大小,但也可以使代码在运行时更有效。

  3. 编译器可以消除调用函数的位,甚至是其他内联函数,因为它可以看到函数对数据执行的操作。 (假设代码检查返回值并调用错误函数,如果它为NULL,则可以将其排除在外。

  4. 它可以减少通话开销,但在目前具有预测分支的处理器中,并不像您想象的那样获胜。

  5. 它可以提升循环中的常量位,进行常见的子表达式消除,以及许多其他优化,使循环代码更有效,例如。

  6. 然后有缺点:

    1. 显然可以使代码更大。

    2. 它可能会增加调用函数内的寄存器压力,这可能会使编译器混淆并阻止它进行优化。

    3. 拥有一个可以存在于CPU缓存中的热门功能可以比将其复制到许多并非总是缓存的地方更快。

    4. 它可能妨碍调试。

    5. 内联函数只是一个提示的原因主要是因为C标准不要求编译器优化任何东西。如果它不是提示,则优化不是可选的。另外,仅仅因为函数不是标记为内联,如果它计算出这样做是有利的,则不会阻止编译器内联。

答案 1 :(得分:6)

通常编译器会选择内联,如果它决定性能会更好。因此,必须比较实际调用非内联函数(将参数推送到堆栈,保存寄存器等)的成本比代码本身更多。因此,对于简单的访问器或简单的计算,通常情况下调用函数的成本更高,因此编译器将内联。

编译器通常也有大小限制,因此它们不允许内联过多地增加代码。

还有一些地方需要考虑。内联函数不太可能导致页面错误,可能不会导致处理器缓存未命中,因此从这个角度来看通常会更快。非内联函数通常需要将代码加载到处理器缓存中,相比之下这是非常慢的。

将函数声明为内联还有其他原因,即使您并不希望编译器内联它们。 C ++中的整个STL都是内联的,尽管大多数STL实际上都不是内联的。这是出于模板实例化的原因,以及与应用程序二进制接口等有关的问题。

答案 2 :(得分:2)

内联函数的好处是消除了函数调用开销,并为优化器提供了使用函数代码优化调用代码的更好机会。例如,优化器可能会在某些情况下完全消除内联代码。

缺点是增加代码大小的可能性,如果瓶颈是CPU指令获取,在某些情况下也会降低执行速度。

所以这取决于。编译器使用各种启发式方法来确定内联函数是否是净收益。即使您没有指定内联关键字,编译器也可以内联函数,如果您这样做,它可能不会内联。通常有一些方法可以强制编译器内联函数来覆盖它的决定。

这个What are good heuristics for inlining functions?有一个很好的讨论和关于内联启发式的进一步链接。

答案 3 :(得分:1)

当你使用内联函数时,函数代码在被调用时被粘贴到行中,它生成的可执行程序也比不使用内联函数的程序大,另一方面,没有内联函数,当调用时,程序停止并跳转到功能代码开始的内存目录...

结论: 内联函数=更好的性能,更多的空间 功能=空间减少,性能稍差

答案 4 :(得分:1)

gcc的内联选项和GNU GCC的决策流程有很好的概述。

这标识了无法内联的情况:“使用varargs,使用alloca,使用可变大小的数据类型,使用计算goto,使用非本地goto和嵌套函数”

重要的一点是非静态函数,即可以调用具有extern链接的函数(这是一个没有static声明的每个函数),或者将其地址放在不同的源中文件。

这会强制编译器生成“正常”函数以及任何内联函数体。当然,这会生成一个程序,该程序比仅生成“正常”非内联函数更大。

Gcc有一个选项-ffunction-sections,它生成目标文件,使链接器能够消除未使用的函数,代价是“...汇编器和链接器将创建更大的对象和可执行文件,并且还将慢一点。“

在更新版本的gcc上,支持链接时优化(LTO)(请参阅"Whole Program Optimizations")。这允许优化阶段查看所有编译的程序,并进行更积极的内联和优化,并排除未使用的代码。

查看一些较新的gcc-4.6 gcc-4.7过程间优化也很有意思。例如,仅内联内联函数的“热路径”,而不是整个函数。 Gcc也可能生成一个函数的多个实例,因为常量是已知的,并且gcc计算最好有多个实现,每个实现针对那些已知常量进行优化。

gcc中有一些选项要求编译器内联所有“足够简单”的函数。

最后它说“根据ISO C ++的要求,GCC认为在类的主体内定义的成员函数被标记为内联,即使它们没有使用inline关键字显式声明”。在这种情况下,使用“足够简单”的规则。

总结:编译器可能会在普通函数或内联函数之上进行非常聪明的优化。