4

我正在研究宏,并发现了许多关于宏和内联函数之间差异的来源和问题。但是没有什么可以具体说明和区分内联函数与普通函数的优缺点。

但是如果我想在普通函数和内联函数之间进行选择呢?

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

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

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

4

5 回答 5

10

内联函数有几个优点:

  1. 它可以使程序体积更小。这通常是一个函数只使用一次的情况。另见 2. 和 3。

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

  3. 编译器可以消除调用函数的位,甚至是其他内联函数,因为它可以看到函数对数据的作用。(假设代码检查返回值并在它为 NULL 时调用错误函数,它可能能够排除这种情况。

  4. 它可以减少调用开销,但在当前具有预测分支的处理器中,这并没有你想象的那么大。

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

然后是缺点:

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

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

  3. 拥有一个可以存在于 CPU 缓存中的热函数比将其复制到许多并不总是缓存的地方要快。

  4. 它可能会妨碍调试。

内联函数只是一个提示的原因主要是因为 C 标准根本不要求编译器优化任何东西。如果它不是一个提示,那么优化就不是可选的。此外,仅仅因为函数没有被标记为内联并不会停止编译器内联它,如果它计算出这样做是有利的。

于 2012-04-12T19:47:57.133 回答
6

一般来说,如果编译器认为性能会更好,它会选择内联。因此,它必须比较实际调用非内联函数(将参数推入堆栈、保存寄存器等)的成本,而不是代码本身。因此,对于简单的访问器或简单的计算,调用函数的成本通常更高,因此编译器将内联。

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

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

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

于 2012-04-12T19:34:58.300 回答
2

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

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

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

对于内联函数有什么好的启发式方法?对内联启发式有很好的讨论和进一步的链接。

于 2012-04-12T19:31:33.643 回答
1

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

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

于 2012-04-12T19:30:29.547 回答
1

对 gcc 的内联选项和GNU GCC的决策过程有一个很好的概述。

这确定了不可能内联的情况:“使用 varargs、使用 alloca、使用可变大小的数据类型、使用计算的 goto、使用非本地 goto 和嵌套函数”

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

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

Gcc 有一个-ffunction-sections生成目标文件的选项,它使链接器能够消除未使用的函数,但代价是“......汇编器和链接器将创建更大的目标文件和可执行文件,而且速度也会变慢。”

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

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

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

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

总结:编译器可能会做非常聪明的优化,超出普通函数或内联函数的预期。

于 2012-04-12T19:49:01.017 回答