Stack- vs heap-allocated buffer in io.Copy

210 views
Skip to first unread message

Joshua Liebow-Feeser

unread,
Apr 29, 2014, 3:43:32 PM4/29/14
to golan...@googlegroups.com
io.Copy uses an internal 32KiB buffer. I would expect it to be stack allocated by doing something like:

var tmp [32*1024]byte
buf := tmp[:]

Instead, they heap-allocate the buffer:

buf := make([]byte, 32*1024)

(see http://golang.org/src/pkg/io/io.go#L350).

I'm wondering if anyone knows what drove this decision or has an idea of why one might do this? My first-pass guess is that the buffer isn't always used, and so allocating it dynamically saves wasted stack space in cases where it's not used, although that strikes me as a tradeoff that's not worth it for the allocation time costs.

Cheers,
Josh

Rob Pike

unread,
Apr 29, 2014, 3:59:37 PM4/29/14
to Joshua Liebow-Feeser, golan...@googlegroups.com
The compiler decides whether something is on the heap or on the stack.
It's not the declaration that determines this. That said, large things
like this move to the heap anyway, although I'm not sure what the
exact cutover is, but it's probably less than 32K.

Regardless, big things like this in such a common routine are better
recycled by the heap than the stack. If you have lots of goroutines
using io.Copy, I suspect the footprint is likelier to be smaller with
heap allocation than stack; it certainly won't be more.

I don't know what the actual thought process was in this code; it may
have been no more than "big things must be allocated". Or it might
just be a habit from an old C programmer.

The real lesson: it doesn't matter, it's taken care of, and don't
worry about it.

-rob

minux

unread,
Apr 29, 2014, 4:00:34 PM4/29/14
to Joshua Liebow-Feeser, golang-nuts
make doesn't necessarily mean the buffer is heap allocated, and var tmp [32*1024]byte
doesn't necessarily mean stacked allocation either.

It's Go's goal to isolate you to the detail. The compiler should take
care of the details. And it will try hard to allocate object on stack.

Rob Pike

unread,
Apr 29, 2014, 4:02:18 PM4/29/14
to Joshua Liebow-Feeser, golan...@googlegroups.com
Ha ha. Turns out the cutoff is 10MB. But in any case, this buffer is
used through an interface so, like most large buffers, it escapes to
the heap anyway.

-rob

minux

unread,
Apr 29, 2014, 4:04:07 PM4/29/14
to Rob Pike, Joshua Liebow-Feeser, golan...@googlegroups.com
On Tue, Apr 29, 2014 at 3:59 PM, Rob Pike <r...@golang.org> wrote:
The compiler decides whether something is on the heap or on the stack.
It's not the declaration that determines this. That said, large things
like this move to the heap anyway, although I'm not sure what the
exact cutover is, but it's probably less than 32K.
For Go 1.3, the cutover seems to be 64KB.

Joshua Liebow-Feeser

unread,
Apr 29, 2014, 4:27:21 PM4/29/14
to golan...@googlegroups.com, Joshua Liebow-Feeser
OK, makes sense. Why is it that stack allocation leads to a potentially larger memory footprint?

Rob Pike

unread,
Apr 29, 2014, 6:11:31 PM4/29/14
to Joshua Liebow-Feeser, golan...@googlegroups.com
Because all the goroutines that would otherwise occupy 4K even if
they're not using the buffer would suddenly need 32K. Whereas when
it's heap allocated, only those buffers in use take space.

-rob

Joshua Liebow-Feeser

unread,
Apr 29, 2014, 7:51:22 PM4/29/14
to Rob Pike, golan...@googlegroups.com
Oh ok sure.
Reply all
Reply to author
Forward
0 new messages