翻译原文连接 转帖/转载请注明出处函数
英文原文连接 发表于2014/06/07post
一个函数调用有三个步骤。建立一个新的堆栈框(stack frame)并把调用者的详细信息记录下来。把任何会被被调用函数用到的寄存器内容保存到堆栈。计算被调用函数的地址,并执行跳转指令到那个新的地址。优化
由于函数调用是频繁操做,CPU的设计者花费了不少精力来优化这个过程,但他们不可能消除全部的开销。spa
根据被调用函数的功能,这个调用开销多是能够忽略不计的,也多是很是显著的。有一个下降调用开销的优化技术叫内联(inlining)。.net
Go语言编译器经过把被调用函数代码看成调用者代码的一部分来实现内联。内联也是有代价的。它会增长编译出来的二进制可执行文件的大小。只有在调用函数的开销占到被调用函数自己的工做量很大一部分的时候,内联才有意义。因此只有简单的函数才被考虑启用内联。调用函数的开销每每不占复杂函数的大头,因此他们也就不会被内联。翻译
上面这个例子展现了函数Double对util.Max的调用。为了下降调用util.Max的成本,编译器会把util.Max内联到Double函数里,产生以下内容:设计
内联以后,util.Max将不会被调用,可是Double的行为并无改变。内联并非Go语言独有的。几乎全部编译的或者即时编译(JITed)的语言会提供这项优化。那么Go语言里的内联是怎么工做的呢?code
Go语言的实现很是简单。当一个包(package)被编译的时候,任何适合内联的小函数都被标记而且按正常状况编译。而后将源代码和编译后的二进制同时保存下来。图片
上面的图片显示了util.a的内容。源代码被作了稍微的改动以方便编译器的快速处理。当编译器编译Double的时候,它会发现util.Max是能够内联的而且util.Max的源代码也存在。这时编译器会插入原函数的源代码,而不是插入一个util.Max的调用。get
保存源代码还使得其它优化成为可能。
好比上面这个例子,虽然Test函数老是返回false,Expensive在执行它以前是没法知道的。可是当Test被内联的时候,咱们就获得了以下的代码:
这样编译器就能知道那块代码是不会被执行到的。
这样不只节省了调用Test函数的开销,它还节省了编译任何不会被执行的代码。Go编译器可以自动在多个文件或者包(package)之间实现函数内联。若是某些代码调用了来自标准库的可内联函数,Go编译器一样能够将这些函数内联进来。