I wonder if this is to do with method values. According to the spec
<https://golang.org/ref/spec#Method_values>, when you declare a method
value like x.M:
The expression x is evaluated and saved during the evaluation of the method
> value; the saved copy is then used as the receiver in any calls, which may
> be executed later.
So using the method value opts.isDigit in index1 does in fact result in
&opts being copied. Maybe this causes opts to escape to the heap (although
I don't know why the copy would need to live beyond the scope of index1).
This would also explain why opts does not escape in index2 where
opts.isDigit() is just a normal method call.
I tested this theory with two new functions (neither of which call
IndexFunc -- that doesn't seem to be part of the problem). One function
calls the isDigit method directly and the other uses a method value.
They're functionally equivalent but opts only escapes in the second
function.
// isDigit called directly: opts does not escape to heap
func isDigit1(r rune, opts options) bool {
return opts.isDigit(r)
}
// isDigit called via method value: opts escapes to heap
func isDigit2(r rune, opts options) bool {
f := opts.isDigit
return f(r)
}
Does anyone have any insight/views on a) whether this is really what's
happening and b) whether this is the desired behaviour? I don't see why
using method values in this way should cause a heap allocation but perhaps
there's a reason for it.
On Tuesday, September 4, 2018 at 4:46:09 PM UTC-5, Paul D wrote:
>
> I'm trying to reduce allocations (and improve performance) in some Go
> code. There's a recurring pattern in the code where a struct is passed to a
> function, and the function passes one of the struct's methods to
> strings.IndexFunc. For some reason, this causes the entire struct to escape
> to the heap. If I wrap the method call in an anonymous function, the struct
> does not escape and the benchmarks run about 30% faster.
>
> Here is a minimal example. In the actual code, the struct has more
> fields/methods and the function in question actually does something. But
> this sample code illustrates the problem. Why does the opts argument escape
> to the heap in index1 but not in the functionally equivalent index2? And is
> there a robust way to ensure that it stays on the stack?
>
>
> type options struct {
> zero rune
> }
>
> func (opts *options) isDigit(r rune) bool {
> r -= opts.zero
> return r >= 0 && r <= 9
> }
>
> // opts escapes to heap
> func index1(s string, opts options) int {
> return strings.IndexFunc(s, opts.isDigit)
> }
>
> // opts does not escape to heap
> func index2(s string, opts options) int {
> return strings.IndexFunc(s, func(r rune) bool {
> return opts.isDigit(r)
> })
> }
>
>
> FYI I'm running Go 1.10.3 on Linux. Thanks...
>
>
>
--
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 [email protected].
For more options, visit https://groups.google.com/d/optout.