激发代码内联 [英] Stimulate code-inlining

查看:114
本文介绍了激发代码内联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

与像C ++这样的语言不同,在C ++语言中,可以显式声明 inline ,在Go中,编译器动态检测可用于内联的函数(C ++也可以这样做,但Go不能同时做)。另外还有一个调试选项来查看可能的内联发生,但很少有网上记录有关go编译器的确切逻辑。

需要在每n个周期重新运行一些大循环;

  func加密(password [] byte)([ ] byte,error){
return bcrypt.GenerateFromPassword(password,13)
}

for id,data:= range someDataSet {
newPassword,_:=加密([] byte(generatedSomething))
data [password] = newPassword
someSaveCall(id,data)
}

Encrypt 为例,以正确内联,我需要考虑哪些逻辑编译器?



我从C ++知道,通过引用传递会增加自动内联的可能性,而不需要明确的 inline 关键字,但要理解编译器准确确定的内容并不容易决定是否选择在Go中内联。像PHP这样的脚本语言如果用一个常量 addSomething($ a,$ b)进行循环,那么它的性能会受到极大的影响, $ c> $ a + $ b (inline)几乎是荒谬的。

解决方案

引用minux (于2013-01-31): p>


默认情况下,内联器将尝试内联叶函数(不调用其他函数/方法/接口)恢复或选择或切换或创建闭包或去/延期功能(见下面的例子),并且在表示时少于40个节点(大致对应于40个简单操作)。但请注意,这仅仅描述了gc编译器的当前现状,未来它肯定会有所改进。因此,请尽量不要依赖于此。


在出现性能问题之前,您不应该在意。内嵌与否,它也会这样做。



如果性能确实重要,并且有明显的差异,那么就不要依赖当前(或过去)内联条件,你自己内联(不要把它放在一个单独的函数中)。

这些规则可以在 $ GOROOT / src / cmd / compile / internal / gc /inl.go 文件。您可以使用'l'调试标志来控制它的攻击性。

 内联工具进行2次传递:首先caninl确定哪些
//函数适合内联,对于那些
//则保存正文的副本。然后inlcalls将每个函数体遍历到
//将调用扩展为inlinable函数。
//
// debug ['l']标志控制攻击性。请注意,main()交换级别0和1,
//使1为默认值,-l禁用。 -ll和更多内容对于清除错误非常有用。
//这些额外的级别(超出-l)可能有问题,不支持。
// 0:禁用
// 1:40节点叶函数,oneliners,惰性类型检查(默认)
// 2:早期检测所有导入的实体
// 3:允许可变参数
// 4:允许非叶子函数,(中断runtime.Caller)
//
//在某些时候,这可能会得到另一个默认值并且变为可关闭与-N。
//
//调试['m']标志启用诊断输出。单个-m对于验证
// //哪些调用是否内联是很有用的,更多用于调试,并且可能随时消失。

同时查看博文:(2014-06-07)写了关于内联



另外有趣的关于内联改进的讨论(可能是Go 1.9?): cmd / compile:改进内联成本模型#17566


Unlike in languages like C++, where you can explicitly state inline, in Go the compiler dynamically detects functions that are candidate for inlining (which C++ can do too, but Go can't do both). Also there's a debug option to see possible inlining happening, yet there is very few documented online about the exact logic of the go compiler(s) doing this.

Let's say I need to rerun some big loop over a set of data every n-period;

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)
}

Aiming for example for Encrypt to being inlined properly what logic should I need to take into consideration for the compiler?

I know from C++ that passing by reference will increase likeliness for automatic inlining without the explicit inline keyword, but it's not very easy to understand what the compiler exactly does to determine the decisions on choosing to inline or not in Go. Scriptlanguages like PHP for example suffer immensely if you do a loop with a constant addSomething($a, $b) where benchmarking such a billion cycles the cost of it versus $a + $b (inline) is almost ridiculous.

解决方案

Quoting minux (on 2013-01-31):

By default, the inliner will try to inline leaf function (doesn't call other functions/method/interfaces) that doesn't call panic or recover or select or switch or create closure or go/defer functions (see example below) and which is less than 40 nodes when represented (roughly corresponding to 40 simple operations). But please beware that this only describes the current status quo of the gc compiler, and it will surely improve in the future. Thus please try not to depend on this.

Until you have performance problems, you shouldn't care. Inlined or not, it will do the same.

If performance does matter and it makes a noticable and significant difference, then don't rely on current (or past) inlining conditions, "inline" it yourself (do not put it in a separate function).

The rules can be found in the $GOROOT/src/cmd/compile/internal/gc/inl.go file. You may control its aggressiveness with the 'l' debug flag.

// 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.

Also check out blog post: Dave Cheney - Five things that make Go fast (2014-06-07) which writes about inlining (long post, it's about in the middle, search for the "inline" word).

Also interesting discussion about inlining improvements (maybe Go 1.9?): cmd/compile: improve inlining cost model #17566

这篇关于激发代码内联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆