Allocation optimization (stack vs. heap)

2,854 views
Skip to first unread message

David Thomas

unread,
Feb 12, 2014, 2:06:12 PM2/12/14
to golan...@googlegroups.com
If I had the following function (which is useless, I know, but that's not the point here):

func f() int {
    buf := make([]byte, 1024)
    return len(buf)
}

If the compiler discovers through escape analysis that buf does not escape, shouldn't it be able to generate code equivalent to the following as an optimization?

func f2() int {
    var bufArray [1024]byte
    buf := bufArray[:]
    return len(buf)
}

Of course in this case the compiler could do much more than that, but I think my point is clear. The call to make() could be replaced with a stack-allocated array.

Does the compiler currently do this? If not, shouldn't it? Is there something I'm missing that would make this harder than other optimizations currently done based on escape analysis?


andrey mirtchovski

unread,
Feb 12, 2014, 2:11:13 PM2/12/14
to David Thomas, golang-nuts
you can check for yourself:

$ cat t.go
package test

func f() int {
buf := make([]byte, 1024)
return len(buf)
}
$ go build -gcflags=-m t.go
# command-line-arguments
./t.go:3: can inline f
./t.go:4: f make([]byte, 1024) does not escape
$

David Thomas

unread,
Feb 12, 2014, 7:06:59 PM2/12/14
to golan...@googlegroups.com, David Thomas
That tells me that the compiler realizes that the result of make([]byte, 1024) doesn't escape. My question was, what does it do with this information? Does it the byte slice's backing array to be allocated on the stack instead of the heap?

After looking at the generated assembly for a few different variations of a function like this, I've realized that the compiler DOES make this optimization already. But the -m flag only tells us that the compiler will consider the optimization. The decision to allocate it on the stack or heap seems to depend on the buffer size.

In particular, for a function like f above, a buffer size of 1024 compiled into something like f2 above. But when I changed 1024 to 65536, it actually called makeslice to put it on the heap.

Dave Cheney

unread,
Feb 12, 2014, 7:18:06 PM2/12/14
to David Thomas, golang-nuts
On Thu, Feb 13, 2014 at 11:06 AM, David Thomas <davidth...@gmail.com> wrote:
That tells me that the compiler realizes that the result of make([]byte, 1024) doesn't escape. My question was, what does it do with this information? Does it the byte slice's backing array to be allocated on the stack instead of the heap?
 
Yes 


After looking at the generated assembly for a few different variations of a function like this, I've realized that the compiler DOES make this optimization already. But the -m flag only tells us that the compiler will consider the optimization. The decision to allocate it on the stack or heap seems to depend on the buffer size.

Yes. As stack frames are taken from the heap anyway, if the local allocation would cause a stack split, and thus an allocation on the heap anyway, the compiler takes the lesser of evils and allocates your large buffer on the heap in an effort to avoid a stack split.
 

In particular, for a function like f above, a buffer size of 1024 compiled into something like f2 above. But when I changed 1024 to 65536, it actually called makeslice to put it on the heap.

On Wednesday, February 12, 2014 2:11:13 PM UTC-5, andrey mirtchovski wrote:
you can check for yourself:

$ cat t.go
package test

func f() int {
     buf := make([]byte, 1024)
     return len(buf)
}
$ go build -gcflags=-m t.go
# command-line-arguments
./t.go:3: can inline f
./t.go:4: f make([]byte, 1024) does not escape
$

--
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...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

David Thomas

unread,
Feb 13, 2014, 11:33:37 AM2/13/14
to golan...@googlegroups.com, David Thomas
That makes a lot of sense. Thanks.

Now I feel like I really have an answer to my underlying question--I shouldn't worry about declaring a local backing array by hand for performance, since the compiler will make the right decision for me anyway.

Dave Cheney

unread,
Feb 13, 2014, 4:52:21 PM2/13/14
to David Thomas, golan...@googlegroups.com, David Thomas
I think the rules of carpentry apply just as well in this instance; measure, then cut, not the other way around. 

Kevin Gillette

unread,
Feb 14, 2014, 1:31:16 AM2/14/14
to golan...@googlegroups.com, David Thomas
On Wednesday, February 12, 2014 5:18:06 PM UTC-7, Dave Cheney wrote:
Yes. As stack frames are taken from the heap anyway, if the local allocation would cause a stack split, and thus an allocation on the heap anyway, the compiler takes the lesser of evils and allocates your large buffer on the heap in an effort to avoid a stack split.

Do non-escaping heap allocations get marked free when the corresponding stack frame gets unrolled? 

minux

unread,
Feb 14, 2014, 1:53:08 AM2/14/14
to Kevin Gillette, David Thomas, golang-nuts

No. Generally the runtime doesn't explicitly free memory now. (e.g. it will only be freed until the next GC).

Jiří Techet

unread,
Feb 14, 2014, 5:57:09 AM2/14/14
to golan...@googlegroups.com
On Thursday, February 13, 2014 1:18:06 AM UTC+1, Dave Cheney wrote:
Yes. As stack frames are taken from the heap anyway, if the local allocation would cause a stack split, and thus an allocation on the heap anyway, the compiler takes the lesser of evils and allocates your large buffer on the heap in an effort to avoid a stack split.

Just wondering, what are the plans regarding this once segmented stacks are gone in go 1.3? It would be theoretically possible to push everything on the stack then, on the other hand using stack for big arrays might cause quite dramatic stack size fluctuations and the need to reallocate the complete stack more often.

Jiri 

Ian Lance Taylor

unread,
Feb 14, 2014, 9:11:27 AM2/14/14
to Jiří Techet, golang-nuts
I don't think there are any plans to change anything. I think you've
correctly identified the tradeoffs.

Ian
Reply all
Reply to author
Forward
0 new messages