Go 中对栈中函数进行内联
上一篇文章 中我论述了叶子内联leaf inlining是怎样让 Go 编译器减少函数调用的开销的,以及延伸出了跨函数边界的优化的机会。本文中,我要论述内联的限制以及叶子内联与栈中内联mid-stack inlining的对比。 内联的限制 把函数内联到它的调用处消除了调用的开销,为编译器进行其他的优化提供了更好的机会,那么问题来了,既然内联这么好,内联得越多开销就越少, 为什么不尽可能多地内联呢? 内联可能会以增加程序大小换来更快的执行时间。限制内联的最主要原因是,创建许多函数的内联副本会增加编译时间,并导致生成更大的二进制文件的边际效应。即使把内联带来的进一步的优化机会考虑在内,太激进的内联也可能会增加生成的二进制文件的大小和编译时间。 内联收益最大的是 小函数 ,相对于调用它们的开销来说,这些函数做很少的工作。随着函数大小的增长,函数内部做的工作与函数调用的开销相比省下的时间越来越少。函数越大通常越复杂,因此优化其内联形式相对于原地优化的好处会减少。 内联预算 在编译过程中,每个函数的内联能力是用 内联预算 计算的 1 。开销的计算过程可以巧妙地内化,像一元和二元等简单操作,在抽象语法数Abstract Syntax Tree(AST)中通常是每个节点一个单位,更复杂的操作如 make 可能单位更多。考虑下面的例子: package main func small()