我有这样一段代码,它应该不做任何分配,但由于某种原因,它做了。
函数的哪些行进行分配,为什么?
功能:
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))[:]
,但它什么也没改变。
1条答案
按热度按时间gzszwxb41#
当一个值在接口中装箱时,它总是被认为是转义的-即使该值从未在调用堆栈之外使用,Go只是在这一点上停止分析,并认为有人可能已经获得了地址,因此该值必须放在堆上。
由于
Read
接受io.Reader
,Write
接受io.Writer
,因此buf
(传递给两个函数的bytes.Buffer
)必须转义。即使你让这些函数接受具体的类型
bytes.Buffer
(你可能不想这样),这也是不够的,因为Read
调用io.ReadFull
,而io.ReadFull
又接受io.Reader
。顺便说一下,对于
Read
中的其他问题,有一个更简单的解决方案,不需要任何unsafe.Slice
技巧:只需将var b byte
替换为var b [1]byte
(在内存中 * 完全相同 *),将b[:]
传递给ReadFull
,并在使用b
的其他地方使用b[0]
。