我有这段代码,本来应该根本不进行分配,但由于某种原因它确实进行了分配。正如基准测试所说,每个操作发生 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中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号