如果一个函数只从一个地方调用,内联它总是更好吗?
If a function is only called from one place, is it always better to inline it?
如果一个函数只在一个地方使用,并且一些分析显示它没有被内联,那么强制编译器内联它是否总是有性能优势?
很明显 "profile and see"(并且在所讨论的函数的情况下,它确实被证明是一个小的性能提升)。我主要是出于好奇 - 使用相当智能的编译器是否有任何性能缺点?
你 不能 "force" 编译器内联它,除非你正在考虑一些你没有提到的特定于实现的工具,所以问题完全是没有实际意义。
如果您的编译器尚未这样做,那是有原因的。
如果函数只被调用一次,内联它应该没有性能劣势。但是,这并不意味着您应该盲目地内联所有函数。例如,如果有问题的代码是 Linux 内核代码,并且您正在使用 BUG_ON 或 WARN_ON 语句来打印堆栈跟踪,则您不会获得完整的堆栈跟踪包括内联函数。相反,堆栈跟踪仅包含调用函数的名称。
而且,正如另一个答案所解释的那样,"inline" 实际上并不强制编译器内联该函数,它只是对编译器的提示。但是,GCC 中实际上有一个属性 __attribute__((always_inline))
应该强制编译器内联该函数。
不,有明显的例外。以此代码为例:
void do_something_often(void) {
x++;
if (x == 100000000) {
do_a_lot_of_work();
}
}
假设 do_something_often()
经常从很多地方被调用。 do_a_lot_of_work()
很少被调用(每 1 亿个调用中就有一个)。将 do_a_lot_of_work()
内联到 do_something_often()
不会给你带来任何好处。由于 do_something_often()
几乎什么都不做,如果将它内联到调用它的函数中会好得多,并且在极少数情况下他们需要调用 do_a_lot_of_work()
,他们会调用它。这样,他们几乎每次都节省了一个函数调用,并在每个调用站点都节省了代码膨胀。
一个合理的情况是不内联一个函数,即使它只从一个位置被调用,如果对该函数的调用很少而且几乎总是跳过。将函数调用之前的指令和函数调用之后的指令在内存中紧密地保存在一起可能允许将这些指令保存在处理器缓存中,而如果这些指令块在内存中是分开的,那将是不可能的。
编译器仍然可以像使用 goto
一样编译函数调用,避免必须跟踪 return 地址,但是如果编译器已经确定函数调用很少见,因此不花太多时间优化该调用是有意义的。
确保未导出函数定义。如果是,它显然需要编译,这意味着如果你的函数很大,可能 call 不会被内联。 (请记住,内联的是 call,而不是函数。一个函数可能在一个地方被内联并在另一个地方被调用,等等)
因此,即使您知道该函数仅从一个地方调用,编译器也可能不知道。确保将函数的定义隐藏到其他目标文件中,例如通过在匿名命名空间中定义它。
话虽这么说,即使只从一个地方调用它,也不意味着内联它总是一个好主意。如果您的函数很少被调用,它可能会在 CPU 缓存中浪费大量内存。
取决于您编写函数的方式。
在某些情况下,是的!
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,为不同用例生成不同优化代码的唯一方法是内联函数。
不,如果代码是一个很少使用的函数,那么将其从 'hot path' 中移除将是有益的。内联函数将用完缓存 space [指令缓存],无论代码是否实际使用。 LTCG 等工具结合 Profile Guided 优化(在 MSFT 世界中,不确定 Linux)会竭尽全力让很少使用的代码远离热路径,这会产生重大影响
如果一个函数只在一个地方使用,并且一些分析显示它没有被内联,那么强制编译器内联它是否总是有性能优势?
很明显 "profile and see"(并且在所讨论的函数的情况下,它确实被证明是一个小的性能提升)。我主要是出于好奇 - 使用相当智能的编译器是否有任何性能缺点?
你 不能 "force" 编译器内联它,除非你正在考虑一些你没有提到的特定于实现的工具,所以问题完全是没有实际意义。
如果您的编译器尚未这样做,那是有原因的。
如果函数只被调用一次,内联它应该没有性能劣势。但是,这并不意味着您应该盲目地内联所有函数。例如,如果有问题的代码是 Linux 内核代码,并且您正在使用 BUG_ON 或 WARN_ON 语句来打印堆栈跟踪,则您不会获得完整的堆栈跟踪包括内联函数。相反,堆栈跟踪仅包含调用函数的名称。
而且,正如另一个答案所解释的那样,"inline" 实际上并不强制编译器内联该函数,它只是对编译器的提示。但是,GCC 中实际上有一个属性 __attribute__((always_inline))
应该强制编译器内联该函数。
不,有明显的例外。以此代码为例:
void do_something_often(void) {
x++;
if (x == 100000000) {
do_a_lot_of_work();
}
}
假设 do_something_often()
经常从很多地方被调用。 do_a_lot_of_work()
很少被调用(每 1 亿个调用中就有一个)。将 do_a_lot_of_work()
内联到 do_something_often()
不会给你带来任何好处。由于 do_something_often()
几乎什么都不做,如果将它内联到调用它的函数中会好得多,并且在极少数情况下他们需要调用 do_a_lot_of_work()
,他们会调用它。这样,他们几乎每次都节省了一个函数调用,并在每个调用站点都节省了代码膨胀。
一个合理的情况是不内联一个函数,即使它只从一个位置被调用,如果对该函数的调用很少而且几乎总是跳过。将函数调用之前的指令和函数调用之后的指令在内存中紧密地保存在一起可能允许将这些指令保存在处理器缓存中,而如果这些指令块在内存中是分开的,那将是不可能的。
编译器仍然可以像使用 goto
一样编译函数调用,避免必须跟踪 return 地址,但是如果编译器已经确定函数调用很少见,因此不花太多时间优化该调用是有意义的。
确保未导出函数定义。如果是,它显然需要编译,这意味着如果你的函数很大,可能 call 不会被内联。 (请记住,内联的是 call,而不是函数。一个函数可能在一个地方被内联并在另一个地方被调用,等等)
因此,即使您知道该函数仅从一个地方调用,编译器也可能不知道。确保将函数的定义隐藏到其他目标文件中,例如通过在匿名命名空间中定义它。
话虽这么说,即使只从一个地方调用它,也不意味着内联它总是一个好主意。如果您的函数很少被调用,它可能会在 CPU 缓存中浪费大量内存。
取决于您编写函数的方式。
在某些情况下,是的!
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,为不同用例生成不同优化代码的唯一方法是内联函数。
不,如果代码是一个很少使用的函数,那么将其从 'hot path' 中移除将是有益的。内联函数将用完缓存 space [指令缓存],无论代码是否实际使用。 LTCG 等工具结合 Profile Guided 优化(在 MSFT 世界中,不确定 Linux)会竭尽全力让很少使用的代码远离热路径,这会产生重大影响