为了账号安全,请及时绑定邮箱和手机立即绑定

优化堆分配

优化堆分配

Go
波斯汪 2021-11-01 16:32:15
当我在谈论 Go 时,我在谈论 gc 编译器的实现。据我所知,Go 执行逃逸分析。在 Go 代码中经常看到以下习语:func NewFoo() *Foo转义分析会注意到 Foo 转义了 NewFoo 并在堆上分配了 Foo。这个函数也可以写成:func NewFoo(f *Foo)并且会像var f Foo NewFoo(&f)在这种情况下,只要 f 不在其他任何地方转义,f 就可以在堆栈上分配。现在我的实际问题。编译器是否有可能将每个优化foo() *Foo为foo(f *Foo),甚至可能在多个级别上优化,其中每个级别都返回 Foo ?如果不是,这种方法在什么样的情况下会失败?先感谢您。
查看完整描述

2 回答

?
森栏

TA贡献1810条经验 获得超5个赞

在做了一些更多的研究之后,我找到了我想要的东西。

我所描述的显然被称为“返回值优化”并且非常可行,这几乎回答了我关于这是否也可以在 Go 中实现的问题。


查看完整回答
反对 回复 2021-11-01
?
千万里不及你

TA贡献1784条经验 获得超9个赞

(不完全是一个答案,但对于评论来说太大了。)


从评论看来你可能对这个小例子感兴趣:


package main


type Foo struct {

    i, j, k int

}


func NewFoo() *Foo {

    return &Foo{i: 42}

}


func F1() {

    f := NewFoo()

    f.i++

}


func main() {

    F1()

}

在 Go1.5 上运行go build -gcflags="-m"给出:


./new.go:7: can inline NewFoo

./new.go:11: can inline F1

./new.go:12: inlining call to NewFoo

./new.go:16: can inline main

./new.go:17: inlining call to F1

./new.go:17: inlining call to NewFoo

./new.go:8: &Foo literal escapes to heap

./new.go:12: F1 &Foo literal does not escape

./new.go:17: main &Foo literal does not escape

因此,内联NewFoo成F1成main(并说,它可能会进一步直列main如果有人是称呼它)。虽然它确实说NewFoo本身会&Foo逃逸,但内联时并不会逃逸。


输出通过初始化堆栈上的对象而不执行任何函数调用来go build -gcflags="-m -S"确认这一点main。


当然,这是一个非常简单的例子,任何复杂的情况(例如调用fmt.Printwith f)都可能很容易导致它逃逸。一般来说,除非分析告诉您存在问题区域并且您正在尝试优化特定的代码部分,否则您不必担心太多。惯用和可读的代码应该胜过优化。


另请注意,使用go test -bench -benchmem(或最好使用testing.B'sReportAllocs方法)可以报告基准代码的分配,这可以帮助识别比预期/期望的分配更多的事情。


查看完整回答
反对 回复 2021-11-01
  • 2 回答
  • 0 关注
  • 174 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信