Hi Everyone,

https://gist.github.com/rkerno/c875609bdeb2459582609da36b54bf72

I'm struggling to wrap my head around the root cause of this issue.

Accessing certain fields of a struct triggers go to allocate the complete 
struct on the heap.  The key ingredients are:

   - A struct with a contained slice of structs defined inline
   - Calls to functions via a function pointer

What follows is a contrived example to demonstrate / investigate the 
problem.

const (
//
// When requireZeroAllocations == true...
//
//     BenchmarkAllocations-2          27763290                52.28 ns/op 
         0 B/op          0 allocs/op
//
// When requireZeroAllocations == false...
//
//     BenchmarkAllocations-2          14403922               1532.00 ns/op 
     2304 B/op          2 allocs/op
//
requireZeroAllocations = false
)

type (
X struct {
id       uint
key      string
value    any
children []X
useId    useIdFunc
useX     useXFunc
useKey   useKeyFunc
useValue useValueFunc
unused   [128]uint64 // Demonstrates that the whole struct is placed on the 
heap!
}
useIdFunc    = func(id uint)
useXFunc     = func(key string, value any)
useKeyFunc   = func(key string)
useValueFunc = func(value any)
)

func scanX(x X) {
if len(x.children) > 0 {
for i := 0; i < len(x.children); i++ {
scanX(x.children[i])
}
return
}

x.useId(x.id)
if !requireZeroAllocations {
// Why can we access the id field without an allocation, but as soon as we 
access
// the string or any fields the entire struct is placed on the heap?
x.useKey(x.key)
x.useX(x.key, x.value)
x.useValue(x.value)
}
}

func printId(id uint) {
}
func printX(key string, value any) {
}
func printKey(key string) {
}
func printValue(value any) {
}


The benchmark used to demonstrate the behaviour is:

func BenchmarkAllocations(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
scanX(
X{
id:    0,
key:   "root",
value: nil,
children: []X{
{
id:    1,
key:   "level1",
value: nil,
children: []X{
{
id:       2,
key:      "k1",
value:    "v1",
children: nil,
useId:    printId,
useX:     printX,
useKey:   printKey,
useValue: printValue,
},
},
},
},
},
)
}
})
}

Any insights or ideas why go exhibits this behaviour would be appreciated.

Cheers,
Rik

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/3ff0270c-caf0-4a23-9cdf-08bf40e83a33n%40googlegroups.com.

Reply via email to