编译器内联功能有多深?

Pau*_*nta 14 c++ compiler-construction inline function

假设我有一些函数,每个函数大约有两行简单的代码,他们互相称呼:A调用B调用C调用D...调用K.(所以基本上它是一系列短函数调用.)编译器通常会在调用树中深入内联这些函数有多深?

Mat*_* M. 13

这个问题没有意义.

如果你考虑内联及其后果,你会意识到:

  • 避免函数调用(所有寄存器保存/帧调整)
  • 向优化器暴露更多上下文(死存储,死代码,常见子表达式消除......)
  • 复制代码(膨胀指令缓存和可执行文件大小等)

在决定是否内联时,编译器因此在创建的潜在膨胀和预期的速度增益之间执行平衡动作.这种平衡行为受到选项的影响:对于gcc -O3意味着优化速度,同时-Oz意味着优化尺寸,内联它们具有准相反的行为!

因此,重要的不是"嵌套级别",而是指令的数量(可能加权,因为并非所有指令都相等).

这意味着一个简单的转发功能:

int foo(int a, int b) { return foo(a, b, 3); }
Run Code Online (Sandbox Code Playgroud)

从内联的角度看,它基本上是"透明的".

另一方面,计算一百行代码的函数不太可能被内联.除了static只调用一次的自由函数是准系统内联的,因为在这种情况下它不会产生任何重复.

从这两个例子中我们可以预见到启发式行为的表现:

  • 函数的指令越少,对于inling越好
  • 它被称为越少,内联越好

在那之后,它们是您应该能够设置为影响某种或那种方式的参数(MSVC __force_inline强烈暗示inling,gcc因为它们-finline-limit标记为"提高"指令计数的阈值等等)


切线:你知道部分内联吗?

它是在4.6中的gcc中引入的.顾名思义,这个想法是部分内联函数.通常,当函数被"保护"并且可能(在某些情况下)几乎立即返回时,避免函数调用的开销.

例如:

void foo(Bar* x) {
  if (not x) { return; } // null pointer, pfff!

  // ... BIG BLOC OF STATEMENTS ...
}

void bar(Bar* x) {
  // DO 1
  foo(x);
  // DO 2
}
Run Code Online (Sandbox Code Playgroud)

可以"优化"为:

void foo@0(Bar* x) {
  // ... BIG BLOC OF STATEMENTS ...
}

void bar(Bar* x) {
  // DO 1
  if (x) { foo@0(x); }
  // DO 2
}
Run Code Online (Sandbox Code Playgroud)

当然,内联的启发式再次适用,但它们更适用于歧视!


最后,除非您使用WPO(整个程序优化)或LTO(链接时间优化),否则只有在定义与调用站点相同的TU(转换单元)时才能内联函数.


Mys*_*ial 7

我见过编译器内联超过5个函数.但在某些时候,它基本上变成了编译器所做的空间效率权衡.每个编译器在这方面都不同.Visual Studio非常保守,内联.GCC(在-O3下)和英特尔编译器喜欢内联......

  • @Zan Lynx:这大多是正确的.在某些情况下,最好不要内联.如果函数在一个性能关键的循环中并且很少被调用(如陷阱处理程序),那么最好不要内联它,以便保持循环的代码大小.(这有时会让你使用短跳而不是跳远) (3认同)