刺激代码内联

Stimulate code-inlining

与 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 关键字的情况下,通过引用传递会增加自动内联的可能性,但要理解编译器究竟做了什么来确定选择内联或不在围棋中。例如,如果您使用常量 addSomething($a, $b) 进行循环,那么像 PHP 这样的脚本语言就会遭受巨大的损失,其中基准测试这样十亿个循环的成本与 $a + $b (内联)相比几乎是荒谬的。

在遇到性能问题之前,您不应该在意。不管内联与否,它都会做同样的事情。

如果性能确实很重要并且会产生明显的显着差异,那么不要依赖当前(或过去)的内联条件,自己“内联”它(不要将其放在单独的函数中)。

规则可以在 $GOROOT/src/cmd/compile/internal/inline/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 InlineCalls 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. Additional levels (beyond -l) may be buggy and
// are not supported.
//      0: disabled
//      1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default)
//      2: (unassigned)
//      3: (unassigned)
//      4: allow non-leaf functions
//
// At some point this may get another default and become switch-offable with -N.
//
// The -d typcheckinl flag enables early typechecking of all imported bodies,
// which is useful to flush out bugs.
//
// 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.

另请查看博客post: Dave Cheney - Five things that make Go fast (2014-06-07) 里面写了关于内联的内容(长post,就在中间,搜索“inline "字)。

关于内联改进的有趣讨论(也许是 Go 1.9?):cmd/compile: improve inlining cost model #17566

更好的是,不要猜测,测量! 您应该信任编译器并避免尝试猜测其内部工作原理,因为它会从一个版本更改为下一个版本。 编译器、CPU 或缓存可以使用的技巧太多,无法从源代码预测性能。

如果内联使您的代码变得更大以至于它不再适合缓存行,使其比非内联版本慢得多怎么办?缓存位置对性能的影响比分支更大。