函数内联 - 什么是影响性能的例子?

时间:2011-04-27 18:02:07

标签: c++ c inline

传统观点认为,功能内联并不总是有益,甚至可能损害性能:

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

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

但是,我遇到了麻烦,找到了内联实际会伤害性能的具体例子。当然,如果一个问题足以值得警告,那么某个地方的人必定会遇到一个内联是一个问题的例子。所以,我问......

什么是一个好的,具体的代码示例,其中性能实际上受到函数内联的影响?

7 个答案:

答案 0 :(得分:6)

在某些平台上,使用大型内联函数,可以通过引起“远”跳跃而不是相对跳跃来降低性能。内联也可能导致页面错误,操作系统需要将更多代码拖入内存,而不是执行可能已存在的代码(作为子例程)。

某些平台可能针对“近距离代码”优化了跳转指令。这种类型的跳转使用当前位置的有符号偏移量。可以限制签名的偏移,例如127字节。跳远需要更大的指令,因为较长的跳转必须包括绝对地址。更长的指令需要更多时间来执行。

长内联函数可能会扩展可执行文件的长度,以便操作系统需要将新的“页面”拖入内存,称为页面交换。页面交换会降低应用程序的执行速度。

这些是内联代码可能会降低性能的“可能”原因。真实的事实是通过剖析获得的。

答案 1 :(得分:4)

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

答案 2 :(得分:3)

尚未提及的一点是将大函数内联到其他大函数中会导致寄存器溢出过多,不仅会损害编译代码的质量,还会增加内联消除的开销(并且最大化甚至是螺丝)在全球和本地优化heurstics,iirc msdn在__forceinline下有一个警告。其他“构造”(如内联非内置asm)可能会产生不需要的堆栈帧,或者具有特殊对齐要求的内联,甚至是那些只是将堆栈分配推入编译器在堆栈检查分配中推送的范围({{在msvc下1}}。

答案 3 :(得分:2)

我不认为内联会伤害性能,而不是间接涉及代码更大,我认为你描述过。

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

答案 4 :(得分:2)

[参考内联函数]

  

该功能放在代码中,   而不是被称为,类似于   使用宏(概念上)

     

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

     

你应该注意到这并不强迫   编译器来制作这个功能   内联,它会忽略你,如果它   认为这是一个坏主意。同样的   编译器可能决定正常   函数内联为你。

     

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

[引用来自SO用户'Fire Lancer'因此归功于他)

答案 5 :(得分:2)

我没有硬数据支持这一点,但无论如何都是Linux内核(因为问题中引用了“Linux内核风格指南”),代码大小会影响性能,因为内核代码占用了物理内容内存,无论指令缓存如何(内核页面永远不会被分页)。

内核使用的内存页面永久不可用于用户虚拟内存。因此,如果您使用内存页面复制的内联代码具有可疑的好处(对于较大的函数,调用开销通常较小),那么您将对系统产生负面影响而无法获得真正的好处。

答案 6 :(得分:1)

为什么你需要具体的例子来说明内联损伤的表现?这是一个上下文敏感的问题。它取决于许多硬件因素,包括RAM速度,CPU型号,编译器版本和许多其他因素。可以在我的计算机上创建这样的示例,但它仍然比非内联版本更快。反过来,内联可能会启用许多其他编译器优化,否则这些优化将无法执行。因此,即使在代码膨胀导致性能下降的情况下,它也可能使某些编译器能够执行许多其他优化来补偿它。

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

如果您需要一个特定的示例,说明内联可能会损害性能,那么请继续编写。一旦你了解这个理论就不那么难了。

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

当然,必须编写该函数,以便在内联时可以消除很少的功能。如果在内联时,编译器能够消除80%的代码,那么这将减轻您可能采取的性能损失。

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

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

因此,一旦您知道该怎么做,通过内联生成较慢的代码非常简单。但要做到这一点还有很多工作要做,特别是如果你想要任何接近可预测或确定性的结果的话。尽管你付出了努力,明年的编译器或明年的CPU可能再次能够超越你,并从你故意“过度内联”的代码中生成更快的代码。

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

最后一点是,这些警告经常被误导,因为没有什么可以警告的。 因为编译器通常会自行选择要内联的内容,并且最好将inline关键字视为提示,但通常无关紧要是否或不是你试着内联一切。

尽管过度内联可能会影响性能,但过度使用inline关键字通常却不会。

inline关键字具有其他效果,应指导其使用。如果要禁用单定义规则,请使用它,以防止在多个转换单元中定义函数时出现链接器错误。