在PHP中,将“interface{}”类型转换为切片(slice)类型时,会导致额外的堆分配。这是因为在PHP中,接口(interface)是一种抽象的数据类型,而切片是一种动态数组类型。当我们将接口类型转换为切片类型时,PHP需要为切片类型分配额外的内存空间来存储切片的元素。这个额外的堆分配操作会导致内存的额外开销,对于一些内存敏感的应用程序来说,可能会带来性能问题。因此,在进行类型转换时,我们应该注意这个问题,尽量避免不必要的额外堆分配。
问题内容
func benchmarkpool(b *testing.b) {
b.reportallocs()
p := sync.pool{new: func() interface{} {
return make([]byte, 1024)
}}
for i := 0; i < b.n; i++ {
bts := p.get().([]byte)
p.put(bts)
}
}
该基准测试在 go1.19.5 中给出以下输出。
benchmarkpool
benchmarkpool-10 47578498 24.47 ns/op 24 b/op 1 allocs/op
当使用 *[]byte
时,事情会变得不同:
func benchmarkpool(b *testing.b) {
b.reportallocs()
p := sync.pool{new: func() interface{} {
bts := make([]byte, 1024)
return &bts
}}
for i := 0; i < b.n; i++ {
bts := p.get().(*[]byte)
p.put(bts)
}
}
BenchmarkPool
BenchmarkPool-10 142008002 8.581 ns/op 0 B/op 0 allocs/op
似乎将 interface{}
转换回切片会导致额外的堆分配。
为什么 go 需要这个额外的分配?其背后的设计考虑是什么?
解决方法
造成分配的不是 any
到 []byte
的转换,而是 []byte
到 any
的转换。 p.Put(bts)
将参数 bts
隐式转换为 any
,然后再将其传递给 (*sync.Pool).Put
。 GoGC 1.19 中的接口被实现为一对指针,一个指向类型元数据,一个指向实际对象,在这种情况下,第二个指针转义到池,导致分配切片对象。这不仅适用于切片类型,也适用于任何其他非指针类型。
对于指针,例如 *[]byte
,编译器会执行优化,将其值直接放入 iface
结构中,从而在转换为接口时删除 *[]byte
实例的分配。因此,通常建议将指针放入池中而不是结构本身。
以上就是为什么将“interface{}”转换回切片会导致额外的堆分配?的详细内容,更多请关注编程网其它相关文章!