THE MID-STACK INLINER
直译为“中栈内联”,属于一种更为新进的内联策略。内联(InLining)的工作原理是将对一个函数的调用展开为函数本身的代码,通过内联减少函数调用的开销,也给编译器带来进一步优化代码的机会。那么,通过内联具体可以节省哪些调用开销呢?
函数被调用、入参、出参、内部局部变量的创建都存在系统开销,不过我想强调的是:内联对虚拟调用的开销优化。内联可以帮助消除动态绑定的开销,通过在编译时确定具体的类型和方法,就不需要在运行时通过虚拟表(v-table)查找具体的函数地址。Go的虚拟调用主要体现在接口类型上,接口的断言或反射是比较耗费性能的,通过内联可以减少反射上的性能开销。
Go编译器会基于一套复杂的规则和模型来决定是否要对函数做内联,这个过程是Go编译时自动帮我们完成的。但作为代码开发者的我们,有没有一种编程的方式方法,主动引导编译器对某些函数进行内联优化呢?
文章的内容主要参考 EFFICIENT GO APIS WITH THE MID-STACK INLINER
,Go版本环境为 1.21.9,以一种更简单的方式来深入了解内联优化。建议你可以跳转阅读原文,本篇可以让你有更加深入的理解。
Go API 设计
很常见的一个问题:虽然实现的是同一个功能,但不同的开发者有不同的写法(每种不同的写法,都是一种API设计)。下面从两个角度来设计实现:从整数类型的切片中获取偶数的功能
返回一个新的切片
这种模式相对比较常见,在函数内部声明一个空的切片类型变量,将偶数追加到这个变量中,最后返回,代码示例如下截图。代码中的 even
就是声明的新变量,最终作为返回值返回。
这种模式的问题在于:变量 even
会发生逃逸,最终在堆上进行内存分配