我有這段程式碼,本來應該根本不進行分配,但由於某種原因它確實進行了分配。正如基準測試所說,每個操作發生 2 次分配。
函數的哪幾行進行分配?為什麼?
功能:
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 }
基準:
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() } }
我想buf
切片以某種方式逃逸,但我不知道如何逃逸,因為據我了解,在這種情況下切片是在堆疊上分配的結構,它將指向變數b
作為其數據。我嘗試將表達式unsafe.Slice(&b, 1)
更改為(*[1]byte)(unsafe.Pointer(&b))[:]
,但它沒有任何改變。
當一個值被裝箱在介面中時,它總是被認為是逃逸的-即使該值從未在呼叫堆疊之外使用過,Go 也會在此時停止分析並認為有人可能已經掌握了該地址,因此該值必須存放在堆上。
由於Read
採用io.Reader
和Write
採用io.Writer
,因此buf
(這是傳遞給這兩個函數的bytes.Buffer
)必須轉義。
即使您讓這些函數採用具體類型bytes.Buffer
(您可能不想要),但這還不夠,因為Read
呼叫io.ReadFull
,它又採用io.Reader
。您必須比這更加努力才能使此分配免於分配。
作為旁注,對於Read
中的其他問題,有一個更簡單的解決方案,不需要任何unsafe.Slice
惡作劇:只需將var b byte
替換為var b [1]byte
(這正是記憶體中相同),將b[:]
傳遞給ReadFull
,並在其他使用b
的地方使用b[0]
。
以上是什麼逃逸到堆中?的詳細內容。更多資訊請關注PHP中文網其他相關文章!