Yes, I came across that post when looking for info on method values and 
allocations. I'm sure that's the root of the problem. But I don't 
understand why the compiler can't figure out that no heap allocation is 
needed. If t doesn't escape when calling t.M(), why can't the compiler work 
out that t.M shouldn't cause t to escape either?

On Thursday, September 6, 2018 at 11:39:41 AM UTC-5, Tristan Colgate wrote:
>
> Perhaps...
>
> https://groups.google.com/forum/m/#!topic/golang-nuts/aMicHmoOH1c
>
> On Thu, 6 Sep 2018, 17:26 , <paul...@gmail.com <javascript:>> wrote:
>
>> Using a pointer receiver (as in your noEscape example) just pushes the 
>> problem up the stack. When you try to call it, e.g.
>>
>>
>> func parent() bool {
>>     var opts options
>>     return noEscape('0', &opts)
>> }
>>
>>
>> you find that &opts escapes to the heap in the parent function instead.
>>
>> I haven't opened an issue yet (I was hoping to get confirmation that it 
>> was a bug first) but will do so today unless someone posts a definitive 
>> answer here.
>>
>> Thanks...
>>
>>
>> On Thursday, September 6, 2018 at 10:33:17 AM UTC-5, Tristan Colgate 
>> wrote:
>>>
>>>   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 <paul...@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...@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...@googlegroups.com <javascript:>.
>> 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