I think this has to do with the pointer reciever, vs the pass by value:

func noEscape(r rune, opts *options) bool {
 f := opts.isDigit
 return f(r)
}

opts here does not escape, but in:

func escapes(r rune, opts options) bool {
 f := opts.isDigit
 return f(r)
}

opts is copied, so it is the copy of opts that the compiler believes
escapes. Perhaps this is because opts could be used by a defer (there is
none though, the compiler could/should notice that).

In the following, opts2 even escapes and gets heap allocated.

func escapes(r rune, opts *options) bool {
  var res bool
  {
    opts2 := *opts

    f := opts2.isDigit
    res = f(r)
  }
  return res
}

Did you open an issue? I'm curious if there is a reason the escape analysis
can't pick this up.


On Wed, 5 Sep 2018 at 18:06 <pauld...@gmail.com> wrote:

> 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.
>

-- 
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