我有这段代码,本来应该根本不进行分配,但由于某种原因它确实进行了分配。正如基准测试所说,每个操作发生 2 次分配。
函数的哪几行进行分配?为什么?
功能:
func (vi *VarInt ) 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 ) 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]
。
以上就是什么逃逸到堆中?的详细内容,更多请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756