假设我有一些函数,每个函数大约有两行简单的代码,它们相互调用如下: A
调用 B
调用 C
调用 D
... 调用 K
。 (所以基本上这是一连串的短函数调用。)编译器通常会在调用树中内联这些函数的深度是多少?
最佳答案
这个问题没有意义。
如果您考虑内联及其后果,您就会意识到:
- 避免函数调用(所有寄存器保存/帧调整)
- 向优化器公开更多上下文(死存储、死代码、公共(public)子表达式消除...)
- 重复代码(膨胀指令缓存和可执行文件大小等)
在决定是否内联时,编译器会在产生的潜在膨胀和预期的速度增益之间进行平衡。这种平衡行为受选项影响:对于 gcc,-O3
表示优化速度,而 -Oz
表示优化大小,在内联时它们具有准相反的行为!
因此,重要的不是“嵌套级别”,而是指令的数量(可能加权,因为并非所有的都是平等的)。
这意味着一个简单的转发函数:
int foo(int a, int b) { return foo(a, b, 3); }
从内联的角度来看基本上是“透明的”。
另一方面,计算一百行代码的函数不太可能内联。除了只调用一次的 static
自由函数是准系统内联的,因为在这种情况下它不会产生任何重复。
从这两个示例中,我们可以预知启发式的行为方式:
- 函数的指令越少,内联效果越好
- 调用越少,内联效果越好
之后,它们是您应该能够设置以影响一种或另一种方式的参数(MSVC as __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
}
可以“优化”为:
void foo@0(Bar* x) {
// ... BIG BLOC OF STATEMENTS ...
}
void bar(Bar* x) {
// DO 1
if (x) { foo@0(x); }
// DO 2
}
当然,内联的启发式方法再次适用,但它们的应用更具歧视性!
最后,除非您使用 WPO(整个程序优化)或 LTO(链接时间优化),否则只有在函数的定义与调用站点位于同一 TU(翻译单元)中时才能内联函数。
关于c++ - 编译器内联函数有多深?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7463034/