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 golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to