FYI, here is the escape analysis output.

 $ go build -gcflags="-l -m=2"

./escape.go:20:10: <S> capturing by ref: opts (addr=true assign=false width=
4)
./escape.go:9:38: (*options).isDigit opts does not escape
./escape.go:15:34: opts escapes to heap
./escape.go:15:34:      from opts.isDigit (call part) at ./escape.go:15:34
./escape.go:14:37: moved to heap: opts
./escape.go:14:37: index1 s does not escape
./escape.go:15:34: index1 opts.isDigit does not escape
./escape.go:18:37: index2 s does not escape
./escape.go:19:30: index2 func literal does not escape
./escape.go:20:14: index2.func1 opts does not escape


Line 15 is the call to IndexFunc from the function index1. I think "call 
part" is supposed to be the reason that opts escapes to the heap but I 
couldn't find any information about what that means.


On Wednesday, September 5, 2018 at 7:03:12 AM UTC-5, Paul D wrote:
>
> Hi Silviu,
>
> Thanks for your reply. I'm not sure about the points you raise though.
>
> Basically, in your index1, the opts.isDigit passed to IndexFunc is 
>> syntactic sugar for (&opts).isDigit -> then the compiler needs to move opts 
>> on the heap.
>
>
> Why? Taking a reference to a function argument shouldn't automatically 
> move it to the heap. As long as the function doesn't store or return the 
> reference, it doesn't exist beyond the scope of that function. Escape 
> analysis should be able to figure that out and leave the value on the 
> stack. My real code passes &opts to several functions without issue. The 
> only ones that cause a problem are the IndexFunc calls.
>
> For your index2 function question, I think it's because Go maintains the 
>> variables from a parent function that are referred in an enclosed function 
>> (closure) for as long as both are alive. 
>> I'm guessing that opts.IsDigit(r) is alive and kicking till it returns.
>
>
> Sorry, I'm not sure what you mean by this. Could you expand on it?
>
> Thanks,
> Paul
>
>
> On Tuesday, September 4, 2018 at 11:05:39 PM UTC-5, Silviu Capota Mera 
> wrote:
>>
>> Hi Paul,
>>
>> Check out: https://golang.org/doc/effective_go.html#pointers_vs_values
>>
>> Basically, in your index1, the opts.isDigit passed to IndexFunc is 
>> syntactic sugar for (&opts).isDigit -> then the compiler needs to move opts 
>> on the heap. 
>>
>> You can either 
>> a) define an alternate isDigitVal method, e.g. func (opts options) 
>> isDigitMethodByVal(r rune) bool
>> or 
>> b) you can just pass the reference to an original *options from the get 
>> go:
>>
>> func index1Pointer(s string, optsPointer *options) int {
>> return strings.IndexFunc(s, optsPointer.isDigit)
>> }
>>
>> Not sure which one will be faster - you'll need to benchmark. Probably 
>> both will be about the same, since that single *options allocation tends to 
>> zero.
>>
>> For your index2 function question, I think it's because Go maintains the 
>> variables from a parent function that are referred in an enclosed function 
>> (closure) for as long as both are alive. 
>> I'm guessing that opts.IsDigit(r) is alive and kicking till it returns. 
>>
>> cheers,
>> silviu
>>
>> On Tuesday, 4 September 2018 17:46:09 UTC-4, 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