函数内联——有哪些影响性能的例子?

Function inlining—what are examples where it hurt performance?

本文关键字:性能 影响 函数      更新时间:2023-10-16

通常认为函数内联并不总是有益的,甚至会损害性能:

  • Linux内核样式指南警告不要过度内联
  • Google还建议程序员小心使用内联
  • c++常见问题解答生活中说了更多相同的

我理解为什么内联被认为是有帮助的——它通过在调用者中包含被调用的函数来消除函数调用的开销。

我也理解为什么人们声称它会损害性能——内联函数在某些情况下会增加代码大小,这最终会增加缓存丢失,甚至触发额外的页面错误。这一切都有意义。

我有麻烦,虽然,找到具体的例子,内联实际上损害性能。当然,如果它是一个值得警告的问题,那么一定有人在某个地方遇到过内联是一个问题的例子。所以,我问…

什么是一个好的,具体的代码例子,其中性能实际上是由函数内联损害的?

在一些使用大型内联函数的平台上,导致"远"跳转而不是相对跳转可能会降低性能。内联还可能导致页面错误,操作系统需要将更多代码装入内存,而不是执行可能已经存在的代码(作为子例程)。

一些平台可能已经为"near code"优化了跳转指令。这种类型的跳转使用当前位置的带符号偏移量。可以限制已签名的偏移量,例如127字节。长距离的跳转需要更大的指令,因为长距离的跳转必须包含绝对地址。较长的指令需要更多的时间来执行。

长内联函数可能会扩展可执行文件的长度,因此操作系统需要在内存中添加一个新的"页面",称为页面交换。页面交换降低了应用程序的执行速度。

这些都是内联代码降低性能的"可能"原因。真正的真相是通过剖析获得的。

我在我们的C (gcc)项目中遇到过这种情况。我的同事在他的库中滥用内联,迫使-fno-inline减少了10%的CPU时间(在带有Ultrasparc IV+处理器的SUN V890上)。

还没有提到的是,将大函数内联到其他大函数中会导致过多的寄存器溢出,不仅会损害编译代码的质量,而且还会增加内联所消除的更多开销(它甚至会搞砸全局和局部优化启发式,iirc msdn在__forceinline下对此有警告)。其他"结构",如内联非裸asm放入内联可能会产生不需要的堆栈帧,或具有特殊对齐要求的内联,甚至那些只是将堆栈分配推入编译器推入堆栈检查分配的范围(msvc下的_chkstk)。

我不认为内联会影响性能,除了间接与代码变大有关,我想你已经描述过了。

一般来说,内联通过消除调用和返回来提高性能。

[参考内联函数]

函数放在代码中,而不是被称为,类似于使用宏(概念上的)

这可以提高速度(没有作用)调用),但会导致代码膨胀(如果功能是使用100次,你现在

你应该注意这并不强制生成函数的编译器Inline,如果是,它会忽略你认为这是个坏主意。同样的编译器可能决定将其设为normal函数内联为您。

这也允许您放置头文件中的整个函数,而不是在CPP中实施文件(无论如何你不能,因为然后你得到一个未解决的外部if它被声明为内联的,除非当然只有那个CPP文件使用了它)。

[引用自SO用户'Fire Lancer',所以归功于他]

我没有硬数据来支持这一点,但在Linux内核的情况下(因为"Linux内核风格指南"在问题中被引用),代码大小可能会影响性能,因为内核代码占用物理内存,而不管指令缓存(内核页面永远不会分页)。

内核使用的内存页对于用户虚拟内存是永久不可用的。因此,如果您为内联代码复制使用内存页,而这些内联代码复制的好处是不确定的(对于较大的函数,调用开销通常很小),那么您正在对系统产生负面影响,而没有真正的好处。

为什么需要内联在哪些方面影响性能的具体例子?这是一个上下文敏感的问题。这取决于许多硬件因素,包括RAM速度、CPU型号、编译器版本和许多其他因素。在我的电脑上创建这样一个例子是可能的,但它仍然会比你的非内联版本快。而内联,反过来,可能会启用几十个其他编译器优化,否则不会被执行。因此,即使在代码膨胀导致性能下降的情况下,它也可能使一些编译器执行许多其他优化来补偿它。

所以你不会得到一个比理论更有意义的答案,为什么会产生较慢的代码。

如果您需要一个特定的例子来说明内联在什么情况下会损害性能,那么请继续编写它。一旦你知道了这个理论就没那么难了。

你想要一个足够大的函数,如果内联的话会污染缓存,并且你想从几个不同但密切相关的地方调用它(如果你从两个完全独立的模块调用它,那么函数的两个实例化无论如何都不会竞争缓存空间)。但是,如果在几个不同的调用站点之间快速交替,则每次实例化都可能迫使前一个实例化出缓存。

当然,在编写函数时必须保证在内联时很少会被删除。如果在内联时,编译器能够消除80%的代码,那么这将减轻您可能遭受的性能损失。

最后,您可能需要强制将其内联。在最好的情况下,编译器倾向于将inline关键字视为提示(有时甚至不是)。因此,您可能需要查找特定于编译器的方法来强制将函数内联。

您可能还想禁用其他优化,因为编译器可能会优化内联版本。

因此,一旦您知道该怎么做,通过内联生成较慢的代码是非常简单的。但要做到这一点需要大量的工作,特别是如果你想要接近可预测或确定性的结果。尽管你付出了努力,明年的编译器或cpu可能会比你更聪明,从你故意"过度内联"的代码中生成更快的代码。

所以我不明白你为什么需要这样做。接受过度的内联在某些情况下会造成伤害,并理解为什么会造成伤害。除此之外,为什么还要麻烦呢?

最后一点是,这些警告往往是被误导的,因为没有什么值得警告的。因为编译器通常会自己选择内联的内容,并且,最多将inline关键字视为提示,所以无论您是否尝试内联所有内容,通常都无关紧要。因此,虽然过度的内联确实会损害性能,但过度使用inline关键字通常不会。

inline关键字还有其他作用,应该指导它的使用。当你想禁用一个定义规则时,可以使用它,以防止在多个翻译单元中定义函数时出现链接器错误。