如果一个函数仅在一个地方使用,而某些分析表明它没有内联,那么在强制编译器内联它时是否总会有性能优势?
显然"简介并看到" (并且在所讨论的功能的情况下,它确实证明是一个小的性能提升)。我主要是出于好奇而提出问题 - 使用相当智能的编译器是否有任何性能上的缺点?
答案 0 :(得分:8)
不,有明显的例外。以此代码为例:
void do_something_often(void) {
x++;
if (x == 100000000) {
do_a_lot_of_work();
}
}
让我们说do_something_often()
经常在很多地方被调用。 do_a_lot_of_work()
被称为极少(每一亿次呼叫中就有一次)。将do_a_lot_of_work()
内联到do_something_often()
并不能获得任何好处。由于do_something_often()
几乎没有任何作用,如果它被内联到调用它的函数中会更好,并且在极少数情况下需要调用do_a_lot_of_work()
,它们会将其称为脱节。通过这种方式,他们几乎每次都保存一个函数调用,并在每个调用站点保存代码膨胀。
答案 1 :(得分:3)
一个合理的情况是不来内联一个函数,即使它只从一个位置调用,如果对该函数的调用很少并几乎总是跳过。将函数调用之前的指令和函数调用之后的指令紧密地保存在内存中可能允许这些指令保存在处理器高速缓存中,如果那些指令块在内存中分离则不可能。
编译器仍然可以像使用goto
一样编译函数调用,避免必须跟踪返回地址,但如果编译器已经确定函数调用很少,那么不花那么多时间来优化这个电话是有意义的。
答案 2 :(得分:1)
你不能“强迫”编译器内联它,除非你正在考虑一些你没有提到过的特定于实现的工具,所以这个问题完全没有实际意义。
如果您的编译器已经没有这样做,那么它有一个原因。
答案 3 :(得分:0)
如果只调用一次函数,则内联函数应该没有性能上的缺点。但是,这并不意味着你应该盲目地内联所有功能。例如,如果所讨论的代码是Linux内核代码,并且您使用BUG_ON或WARN_ON语句来打印堆栈跟踪,则无法获得包含内联函数的完整堆栈跟踪。相反,堆栈跟踪仅包含调用函数的名称。
并且,正如另一个答案所解释的那样," inline"实际上并没有强制编译器内联函数,它只是对编译器的一个提示。但是,GCC中实际上有一个属性__attribute__((always_inline))
,它应该强制编译器内联函数。
答案 4 :(得分:0)
确保未导出功能定义。如果是,显然需要编译,这意味着如果您的函数很大,可能调用将不会内联。 (记住,它是内联的调用,而不是函数。一个函数可能在一个地方内联并在另一个地方调用,等等。)
因此,即使您知道仅从一个地方调用该函数,编译器也可能不会。确保将函数的定义隐藏到其他目标文件中,例如通过在匿名命名空间中定义它。
话虽如此,即使只从一个地方召唤,也并不意味着内联它总是一个好主意。如果很少调用您的函数,则可能会在CPU缓存中浪费大量内存。
答案 5 :(得分:0)
取决于您编写函数的方式。
在某些情况下,是的!
void doSomething(int *src, int *dst,
const int loopCountInner, const int loopCountOuter)
{
int i, j;
for(i=0; i<loopCounterOuter; i++){
for(j=0; j<loopCounterInner; j++){
*dst = someCalculations(*src);
src++;
dst++
}
}
}
在此示例中,如果此函数编译为非内联,则编译器基本上不知道两个循环的行程计数。对于强烈依赖编译时优化的实现来说,这是一个大问题。
我遇到了更糟糕的情况:编译器假设loopCounterInner
是一个较大的值并针对该情况进行了优化,但loopCounterInner
实际上是3或5,所以最好的选择是完全展开内部环!
对于C ++,最好的方法是使它们成为模板变量,但对于C,为不同的用例生成不同优化代码的唯一方法是内联函数。
答案 6 :(得分:0)
不,如果代码是很少使用的功能,那么请将其保持在“热门路径”之外。会有益的。无论代码是否实际使用,内联函数都会占用缓存空间[指令缓存]。像LTCG这样的工具结合Profile Guided优化(在MSFT世界中,对Linux不太确定)非常痛苦地将很少使用的代码保留在热门路径之外,这可以产生显着的差异