Home >Backend Development >Golang >What escapes into the heap?
I have this code that is supposed to not allocate at all, but for some reason it does. As the benchmark says, 2 allocations occur per operation.
Which lines of the function are allocated? Why?
Function:
func (vi *VarInt /* int32 */) Read(input io.Reader) error { var ( b byte buf = unsafe.Slice(&b, 1) shift int value uint32 ) for { _, err := io.ReadFull(input, buf) if err != nil { return err } value |= (uint32(b) & 0b01111111) << shift if (b & 0b10000000) == 0 { *vi = VarInt(value) return nil } shift += 7 if shift >= 32 { return ErrVarIntTooLong } } } func (vi *VarInt /* int32 */) Write(output io.Writer) error { var ( varint [5]byte uvalue = uint32(*vi) x int ) for ; ; x++ { vb := uint8(uvalue) if (vb & 0b10000000) == 0 { varint[x] = vb break } varint[x] = (vb & 0b01111111) | 0b10000000 uvalue >>= 7 } _, err := output.Write(varint[:x+1]) if err != nil { return err } return nil }
Benchmark:
func BenchmarkVarInt(b *testing.B) { var buf bytes.Buffer buf.Grow(5) b.ResetTimer() for i := 0; i < b.N; i++ { vi := (VarInt)(i) vi.Write(&buf) vi.Read(&buf) buf.Reset() } }
I guessbuf
the slice escapes somehow but I don't know how because as I understand the slice in this case is a struct allocated on the stack which will point to a variableb
as its data. I tried changing the expression unsafe.Slice(&b, 1)
to (*[1]byte)(unsafe.Pointer(&b))[:]
but it doesn't have any Change.
When a value is boxed in an interface, it is always considered escaping - even if the value is never in the call stack If it has been used externally, Go will stop analyzing it at this point and think that someone may have obtained the address, so the value must be stored on the heap.
Since Read
takes io.Reader
and Write
takes io.Writer
, therefore buf
(this is that the bytes.Buffer
) passed to these two functions must be escaped.
Even if you make these functions take a concrete type bytes.Buffer
(which you probably don't want), it's not enough because Read
calls io.ReadFull
, which again takes io.Reader
. You have to work harder than that to get this assignment exempt.
As a side note, to the other problem in Read
, there is a simpler solution that doesn't require any unsafe.Slice
shenanigans: just change var b Replace byte
with var b [1]byte
(which is exactly the same in memory), pass b[:]
to ReadFull
, and use b[0]
wherever else b
is used.
The above is the detailed content of What escapes into the heap?. For more information, please follow other related articles on the PHP Chinese website!