与C ++这样的语言不同,你可以在其中明确地陈述inline
,在Go中,编译器动态地检测内联的候选函数(C ++也可以这样做,但Go不能同时执行这两种操作)。还有一个调试选项可以看到可能的内联发生,但是很少有人在线记录go编译器执行此操作的确切逻辑。
假设我需要每n个周期重新运行一组数据的大循环;
func Encrypt(password []byte) ([]byte, error) {
return bcrypt.GenerateFromPassword(password, 13)
}
for id, data := range someDataSet {
newPassword, _ := Encrypt([]byte("generatedSomething"))
data["password"] = newPassword
someSaveCall(id, data)
}
以Encrypt
为例,正确地内联编译器需要考虑哪些逻辑?
我从C ++中知道,通过引用传递将增加自动内联的可能性而不使用显式inline
关键字,但是要理解编译器确定选择内联与否的决策的确切方法并不容易走。例如PHP这样的脚本语言会受到极大的影响,如果你使用常量addSomething($a, $b)
进行循环,其中基准测试如此十亿次的成本与$a + $b
(内联)相比几乎是荒谬的。
答案 0 :(得分:7)
Quoting minux(2013-01-31):
默认情况下,内联器将尝试内联叶函数(不调用其他函数/方法/接口),不会调用恐慌或恢复或选择或切换或创建闭包或转/延函数(参见下面的示例)并且在表示时少于40个节点(大致对应于40个简单操作)。但请注意,这只描述了gc编译器的当前现状,并且将来肯定会有所改进。因此,请尽量不依赖于此。
在遇到性能问题之前,你不应该关心。内联与否,它也会这样做。
如果表现确实重要并且它具有明显的显着差异,那么就不要依赖当前(或过去)的内联条件,"内联"它自己(不要把它放在一个单独的功能)。
可以在$GOROOT/src/cmd/compile/internal/gc/inl.go
文件中找到规则。您可以使用'l'
调试标志来控制其侵略性。
// The inlining facility makes 2 passes: first caninl determines which
// functions are suitable for inlining, and for those that are it
// saves a copy of the body. Then inlcalls walks each function body to
// expand calls to inlinable functions.
//
// The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1,
// making 1 the default and -l disable. -ll and more is useful to flush out bugs.
// These additional levels (beyond -l) may be buggy and are not supported.
// 0: disabled
// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
// 2: early typechecking of all imported bodies
// 3: allow variadic functions
// 4: allow non-leaf functions , (breaks runtime.Caller)
//
// At some point this may get another default and become switch-offable with -N.
//
// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
// which calls get inlined or not, more is for debugging, and may go away at any point.
另请查看博客帖子:Dave Cheney - Five things that make Go fast(2014-06-07),其中写有关于内联的内容(长篇文章,它在中间,搜索"内联"字)。
关于内联改进的有趣讨论(也许是Go 1.9?):cmd/compile: improve inlining cost model #17566
答案 1 :(得分:1)
更好的是,不要猜测,衡量! 您应该信任编译器并避免尝试猜测其内部工作原理,因为它将从一个版本更改为下一个版本。 编译器,CPU或缓存可以起到很多技巧,能够从源代码中预测性能。
如果内联使你的代码更大到不再适合缓存行,那么它会比非内联版本慢得多吗?缓存局部性对性能的影响要大于分支。