go escape analysis

419 views
Skip to first unread message

刘桂祥

unread,
Sep 28, 2016, 10:18:29 AM9/28/16
to golang-nuts
// example1.go
package main


func main
() {

   
for i := 0; i < 2; i++ {
        m
:= make(map[int]int)                                                                                                                                            
        m
[1] = 100
   
}  


}


main make(map[int]int) does not escape


// example2.go
package main


func main
() {
   
var m map[int]int
   
for i := 0; i < 2; i++ {
        m
= make(map[int]int)                                                                                                                                            
        m
[1] = 100
   
}  


}

 make(map[int]int) escapes to heap    why ???


Dave Cheney

unread,
Sep 28, 2016, 10:41:09 AM9/28/16
to golang-nuts
Which version of Go?

刘桂祥

unread,
Sep 28, 2016, 10:56:59 AM9/28/16
to golang-nuts
go 1.7

在 2016年9月28日星期三 UTC+8下午10:41:09,Dave Cheney写道:

Chris Manghane

unread,
Sep 28, 2016, 1:39:09 PM9/28/16
to 刘桂祥, golang-nuts
In the first example, make does not escape the scope of the for statement. In the second example, make is assigned to m, which is outside of the scope of the for statement, which means the make operation escapes its scope and subsequently, is heap allocated. If you want more information about why something escapes, try compiling with -gcflags "-m -m" for an explanation of the escape analysis information.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

unread,
Sep 28, 2016, 2:15:16 PM9/28/16
to golang-nuts
We could discuss what has "gone wrong" in the Go compiler; and how to make it work at least in theory.

Unfortunately, me not being paid by Google is a line I am both unable and unwilling to cross.

I feel sorry for not being able to discuss the subject of escape analysis which by itself is quite fascinating.

Sincerely
atomsymbol

Ian Lance Taylor

unread,
Sep 28, 2016, 2:50:28 PM9/28/16
to ⚛, golang-nuts
On Wed, Sep 28, 2016 at 11:15 AM, ⚛ <0xe2.0x...@gmail.com> wrote:
> We could discuss what has "gone wrong" in the Go compiler; and how to make
> it work at least in theory.
>
> Unfortunately, me not being paid by Google is a line I am both unable and
> unwilling to cross.
>
> I feel sorry for not being able to discuss the subject of escape analysis
> which by itself is quite fascinating.

For the record, in case anybody is confused by this, there are people
who work actively on the Go compiler who are not paid by Google. You
should of course make your own choices.

Ian

刘桂祥

unread,
Sep 29, 2016, 5:32:14 AM9/29/16
to golang-nuts, liuguix...@gmail.com
veyr thanks and want to know more about go's compile and escape rules 

在 2016年9月29日星期四 UTC+8上午1:39:09,Chris Manghane写道:
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Dave Cheney

unread,
Sep 29, 2016, 5:41:48 AM9/29/16
to golang-nuts
-gcflags=-m is your guide. There are no documents of the escape analysis done by the gc compiler, but you could read the source of cmd/compile/internal/gc/esc.go

刘桂祥

unread,
Sep 29, 2016, 11:20:36 PM9/29/16
to golang-nuts, liuguix...@gmail.com
package main

func main() {
    var m map[int]int

    {
        m = make(map[int]int)
    }

    _ = m
}

if I do this m will not escape just want to know what's the scope rule for escape ?  puzzled 

在 2016年9月29日星期四 UTC+8上午1:39:09,Chris Manghane写道:
In the first example, make does not escape the scope of the for statement. In the second example, make is assigned to m, which is outside of the scope of the for statement, which means the make operation escapes its scope and subsequently, is heap allocated. If you want more information about why something escapes, try compiling with -gcflags "-m -m" for an explanation of the escape analysis information.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Axel Wagner

unread,
Sep 30, 2016, 2:42:30 AM9/30/16
to 刘桂祥, golang-nuts
The only real rule is "when the compiler can prove, that it doesn't escape, it doesn't". There is no guarantee or fixed policy around escape analysis.
If you are convinced that something doesn't escape and that the compiler should be able to figure it out, you can try filing an issue about that with the code and output of -gcflags=-m.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Chris Manghane

unread,
Sep 30, 2016, 12:17:15 PM9/30/16
to 刘桂祥, golang-nuts
You're correct. I'm sorry about my first response; it was too brief and led to the confusion you have. The Go escape analysis algorithm has a concept of "loopdepth" (shortened as ld in debug output). The loopdepth is -1 for global variables, 0 for input arguments to a function, 1 for the top level of a function, and is incremented by 1 for each for or label statement encountered to mark a new independent scope. In your most recent example, using `{ }` creates a new scope technically, but for our purposes in escape analysis, we only consider loops and labels as new scope; that means if statements and switch statements are not counted as well. Every expression and statement is given a loopdepth and when walking through the graph of connected nodes, we consider an expression to be leaking if it involves a dereference and the lhs has a lower loopdepth than the rhs (https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/esc.go#L1803).


For the most recent example, this is the output I get from -gcflags "-m -m -m -m":
# command-line-arguments
./ex.go:3: can inline @"".main as: func() { var @"".m·1 map[int]int; ; @"".m·1 = make(map[int]int); _ = @"".m·1 }
./ex.go:4:[1] main esc: m
./ex.go:4:[1] main esc: var m map[int]int
./ex.go:4:[1] main esc: m
./ex.go:4:[1] main esc: m = <N>
./ex.go:7:[1] main esc: m
./ex.go:7:[1] main esc: 0
./ex.go:7:[1] main esc: make(map[int]int)
./ex.go:7:[1] main esc: m = make(map[int]int)
./ex.go:7:[1] main escassign: m( l(4) class(PAUTO) f(1) ld(1) assigned)[NAME] = make(map[int]int)( l(7) esc(no) ld(1))[MAKEMAP]
./ex.go:7::flows:: m <- make(map[int]int)
./ex.go:10:[1] main esc: _
./ex.go:10:[1] main esc: m
./ex.go:10:[1] main esc: _ = m

escflood:0: dst m scope:main[1]
escwalk: level:{0 0} depth:0  op=MAKEMAP make(map[int]int)( l(7) esc(no) ld(1)) scope:main[1] extraloopdepth=-1
./ex.go:7: main make(map[int]int) does not escape

Let's focus on the following line:
./ex.go:7:[1] main escassign: m( l(4) class(PAUTO) f(1) ld(1) assigned)[NAME] = make(map[int]int)( l(7) esc(no) ld(1))[MAKEMAP]

This describes line 7: `m = make(map[int]int)`. Notice that both `m` and `make(map[int]int)` has loopdepth, ld(1). This is not considered to be a leak.

Now let's look at your first escaping example, which you labeled as example2.go:
# command-line-arguments
./example2.go:5:[1] main esc: m
./example2.go:5:[1] main esc: var m map[int]int
./example2.go:5:[1] main esc: m
./example2.go:5:[1] main esc: m = <N>
./example2.go:6:[2] main esc: false
./example2.go:7:[2] main esc: m
./example2.go:7:[2] main esc: 0
./example2.go:7:[2] main esc: make(map[int]int)
./example2.go:7:[2] main esc: m = make(map[int]int)
./example2.go:7:[2] main escassign: m( l(5) class(PAUTO) f(1) ld(1) assigned)[NAME] = make(map[int]int)( l(7) esc(no) ld(2))[MAKEMAP]
./example2.go:7::flows:: m <- make(map[int]int)
./example2.go:6:[1] main esc: for loop
./example2.go:10:[1] main esc: _
./example2.go:10:[1] main esc: m
./example2.go:10:[1] main esc: _ = m

escflood:0: dst m scope:main[1]
escwalk: level:{0 0} depth:0  op=MAKEMAP make(map[int]int)( l(7) esc(no) ld(2)) scope:main[2] extraloopdepth=-1
./example2.go:7: make(map[int]int) escapes to heap
./example2.go:7: from m (assigned) at ./example2.go:5

And focus on the corresponding line:
./example2.go:7:[2] main escassign: m( l(5) class(PAUTO) f(1) ld(1) assigned)[NAME] = make(map[int]int)( l(7) esc(no) ld(2))[MAKEMAP]

Note that the loopdepth, ld, of `m` is 1, and the loopdepth, ld of `make(map[int]int)` is 2. When walking through the MAKEMAP op `escwalk: level:{0 0} depth:0  op=MAKEMAP make(map[int]int)( l(7) esc(no) ld(2)) scope:main[2] extraloopdepth=-1`, we consider `make(map[int]int)` to be leaking and decide to heap allocate it.

I hope that explanation clarifies the decision made by the compiler in these different example. Feel free to ask more questions if you'd like. You can message me directly if you don't want to clutter the mailing list.

Thanks,
Chris

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Chris Manghane

unread,
Sep 30, 2016, 12:19:17 PM9/30/16
to Axel Wagner, 刘桂祥, golang-nuts
Hmm, I'm not so sure if that's the rule. The rules are expressed in the code in https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/esc.go, which is unlikely to make any significant changes in the near future. Those rules aren't written in plain english, but we should not discourage any attempts to do so.

Chris

刘桂祥

unread,
Oct 2, 2016, 4:55:32 AM10/2/16
to golang-nuts, axel.wa...@googlemail.com, liuguix...@gmail.com

Hi  Chris:


          I am gopher from Beijing China . I am very like programing and adore the old hack culture . and NBA too!


        I am very happy in google groups and receive the answer so quickly.  


        go on practice programing and improve my badly english 


     

       question:  


       package main


func main() {

    var a []int


Label1:

    a = make([]int, 0, 2)

    _ = a

}


 could’t you give me an example that happens escape ?


在 2016年10月1日星期六 UTC+8上午12:19:17,Chris Manghane写道:
Reply all
Reply to author
Forward
0 new messages