2011-09-18 60 views
13

说我有一些功能,每个代码大约两个简单的线条,和他们互相称呼这样的:A电话B电话C调用D ...调用K。 (所以基本上这是一系列短的函数调用。)编译器通常会在调用树中嵌入这些函数多深?编译器内联函数有多深?

+0

您可以简单地测试并查看程序集!您的编译器文档应该告诉您如何指定内联深度;我认为默认情况下GCC的值大概是50。 –

+2

我相信这应该是编译器特定的,并且不会发布编译器的信息。 –

+1

在MSVC下,您可以使用'#pragma inline_depth'(http://msdn.microsoft.com/zh-cn/library/cx053bca.aspx)对此进行部分控制,尽管在某些情况下我遇到了问题(例如递归内联,这是有可能的,但从来没有工作,最终做手工) – Necrolis

回答

12

的问题是没有意义的。

如果你仔细想想内联,而其后果,你就会意识到这一点:

  • 避免了函数调用(与所有的寄存器保存/帧调整)
  • 自曝更多背景信息优化(死了商店,死码,公共子表达式elimintation ...)
  • 重复码(腹胀指令缓存和可执行文件的大小,除其他事项外)

决定当无论是否内联,编译器都会在潜在的膨胀和预期的速度增益之间执行平衡操作。这种平衡行为受到选项的影响:对于gcc -O3意味着优化速度,而-Oz意味着对尺寸进行优化,在内联时它们具有准相反的行为!

因此,重要的不是“嵌套层次”是指令的数量(可能加权因为不是所有的都是一样的)。

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

int foo(int a, int b) { return foo(a, b, 3); } 

是从视点内联基本上是“透明的”。

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

从这两个例子,我们得到的启发是如何表现一种预感:

  • 少的指令功能有,用于inling
  • 的次数越少就越好叫法,内联
  • 更好

在那之后,他们的参数,你应该能够设置影响这种或那种方式(MSVC为__force_inline强烈的inling暗示,gcc因为他们-finline-limit标志,以“提高”的tresh旧的指令数等...)


在切线:你知道部分内联

它是在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 [email protected](Bar* x) { 
    // ... BIG BLOC OF STATEMENTS ... 
} 

void bar(Bar* x) { 
    // DO 1 
    if (x) { [email protected](x); } 
    // DO 2 
} 

当然,再次为内联启发式申请,但他们更多的申请有差别!


最后,除非你使用WPO(全程序优化)或LTO(链接时间优化)功能只能如果他们的定义是相同的TU(翻译单元)内联该调用点。

+0

我通常不会这样做,但我认为我应该改变接受的答案。 :)我不知道部分内联,也不知道基于调用次数的内联。感谢细节。 –

7

我见过编译器内联多于5个函数。但在某种程度上,它基本上成为编译器所做的空间效率权衡。每个编译器在这方面都有所不同。 Visual Studio对于内联非常保守。 GCC(-O3下)和英特尔编译爱内联......

+0

IIRC在gcc它取决于一些近似的“指令计数”功能被内联(即它实际上是多久);嵌套层次不起到我在文档('-finline-limit'和朋友)中读取的角色,因为长度为10的函数将内嵌与5 +嵌套5相同。 – eudoxos

+1

如果函数被调用只有一次没有理由避免内联。如果配置文件反馈说明它应该,GCC也会积极地进行内联。 –

+3

@赞恩Lynx:大部分是正确的。有些情况下,最好不要内联。如果函数处于性能关键的循环中,并且很少调用(如陷阱处理程序),那么最好不要内联它,以便将循环的代码大小缩小。 (这有时会让你用短跳而不是长跳) – Mysticial

相关问题