Thanks Robert. I don't think it's the value that's causing this....I still get the allocation if I remove the value from the equation. It's something to do with the root level having children.
The escape analysis from the refactored code below is:
go build -gcflags='-m=2' .
./alloc.go:12:27: parameter y leaks to {heap} with derefs=0:
./alloc.go:12:27: flow: {heap} = y:
./alloc.go:12:27: from y.key (dot) at ./alloc.go:19:6
./alloc.go:12:27: from fn(y.key) (call parameter) at ./alloc.go:19:4
Strings can't reference anything, AFAIK.
BenchmarkAllocations0-2 97209274 28.76 ns/op 0 B/op 0 allocs/op
BenchmarkAllocations1-2 1395814 796.9 ns/op 1152 B/op 1 allocs/op
type (
Y struct {
key string
children []Y
unused [128]uint64 // Demonstrates that the whole struct is placed on the heap!
}
useKeyFunc = func(key string)
)
func scanY(fn useKeyFunc, y Y) {
if y.children != nil {
for i := 0; i < len(y.children); i++ {
scanY(fn, y.children[i])
}
return
}
fn(y.key)
}
func printKey(key string) {
}
func Benchmark_ZeroAllocations(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
scanY(
printKey,
Y{
key: "root",
children: []Y{},
},
)
}
})
}
func Benchmark_OneAllocation(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
scanY(
printKey,
Y{
key: "root",
children: []Y{
{
key: "level1",
children: nil,