Perhaps... https://groups.google.com/forum/m/#!topic/golang-nuts/aMicHmoOH1c
On Thu, 6 Sep 2018, 17:26 , <pauld...@gmail.com> 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+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.