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.