reflect.Call allocates a []reflect.Value slice of length nout for the number of return arguments from the function. This is fine for a one-off call of a function, but when repeatedly invoking a function, reflect.Call allocations can quickly add up.
As an example, I have a helper package that sorts arbitrary types, and if a type has a func (t T) Less(other T) bool (i.e., Name == "Less", NumIn() == 1, NumOut() == 1, In(0) == T, Out(0) == bool), the package calls that function for sorting. This is done within sort.Slice, meaning Less is called as many times as sort.Slice needs to compare. For an ordered slice of 1000 elements, each sort.Slice allocates 25735 times.
The only four allocations in this benchmark are (tip @ 2cf85b1)
105.50MB 68.28% 68.28% 105.50MB 68.28% reflect.Value.call /home/twmb/go/go.tip/src/reflect/value.go:565
41MB 26.53% 94.81% 41MB 26.53% reflect.methodReceiver /home/twmb/go/go.tip/src/reflect/value.go:862
4.50MB 2.91% 97.72% 4.50MB 2.91% reflect.Value.call /home/twmb/go/go.tip/src/reflect/value.go:609
1.50MB 0.97% 98.70% 1.50MB 0.97% runtime.allocm /home/twmb/go/go.tip/src/runtime/proc.go:1866
70% of the allocations are from allocating the output argument slice. If possible, I could provide the input slice and reuse it for all Calls, which would effectively eliminate this alloc.
I looked into line 862 and into the runtime, I'm not sure how to get rid of that allocation: the code takes the address of an unsafe pointer, and that allocates; if possible it would be quite nice to get rid of this alloc as well but this would likely require deeper wiring into the runtime (the point here is to have a method pointer). Line 609 has a TODO that could eliminate the allocation. runtime/proc.go:1866 looks to be unrelated.
reflect.Value.call can be:
func (v Value) call(op string, in, out []Value) []Value {
If out is non-nil, then it is used rather than allocating a new slice. If the input out is too small, the code will panic. Essentially, line 565 turns into:
ret = out
if ret == nil {
ret = make([]Value, nout)
}
if len(ret) < nout { // optional check
panic("short output slice")
}
reflect.Callallocates a[]reflect.Valueslice of lengthnoutfor the number of return arguments from the function. This is fine for a one-off call of a function, but when repeatedly invoking a function,reflect.Callallocations can quickly add up.As an example, I have a helper package that sorts arbitrary types, and if a type has a
func (t T) Less(other T) bool(i.e.,Name == "Less", NumIn() == 1, NumOut() == 1, In(0) == T, Out(0) == bool), the package calls that function for sorting. This is done within sort.Slice, meaningLessis called as many times assort.Sliceneeds to compare. For an ordered slice of 1000 elements, eachsort.Sliceallocates 25735 times.The only four allocations in this benchmark are (tip @ 2cf85b1)
70% of the allocations are from allocating the output argument slice. If possible, I could provide the input slice and reuse it for all
Calls, which would effectively eliminate this alloc.I looked into line 862 and into the runtime, I'm not sure how to get rid of that allocation: the code takes the address of an unsafe pointer, and that allocates; if possible it would be quite nice to get rid of this alloc as well but this would likely require deeper wiring into the runtime (the point here is to have a method pointer). Line 609 has a TODO that could eliminate the allocation. runtime/proc.go:1866 looks to be unrelated.
reflect.Value.callcan be:If out is non-nil, then it is used rather than allocating a new slice. If the input
outis too small, the code will panic. Essentially, line 565 turns into: