What escapes into the heap?

王林
Release: 2024-02-06 10:09:07
forward
1214 people have browsed it

What escapes into the heap?

Question content

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
}
Copy after login

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()
    }
}
Copy after login

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.


Correct answer


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!

source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!