Skip to content

Instantly share code, notes, and snippets.

@mandarjog
Created March 15, 2017 18:28
Show Gist options
  • Save mandarjog/daee012c5c973fecd106f49c786f96cc to your computer and use it in GitHub Desktop.
Save mandarjog/daee012c5c973fecd106f49c786f96cc to your computer and use it in GitHub Desktop.
slice allocation
func (f *Function) Eval(attrs attribute.Bag, fMap map[string]Func) (interface{}, error) {
fn := fMap[f.Name]
if fn == nil {
return nil, fmt.Errorf("unknown function: %s", f.Name)
}
if ufn, ok := fn.(UnmanagedFunc); ok {
return ufn.CallUnmanaged(attrs, f.Args, fMap)
}
// may panic if config is not consistent with Func.ArgTypes().
s := AllocSlice()
//args := make([]interface{}, len(f.Args), len(f.Args))
args := s.s[0:len(f.Args)]
for idx, earg := range f.Args {
arg, err := earg.Eval(attrs, fMap)
if err != nil && !fn.AcceptsNulls() {
return nil, err
}
args[idx] = arg
}
if glog.V(2) {
glog.Infof("calling %#v %#v", fn, args)
}
ret := fn.Call(args)
FreeSlice(s)
return ret, nil
}
@jba
Copy link

jba commented Mar 15, 2017

I think you said you tried something like

var argbuf [MaxArgs]interface{}
args := argbuf[:len(f.Args)]
...

and didn't notice an improvement, right?

I'm guessing that's because when the compiler sees ret := fn.Call(args) it can't be sure that args won't escape, so it must do some heap allocation there. (Note that if args had a stack-allocated argbuf as backing array and it did escape, and Eval returned, then args would point to garbage.)

As far as I can see, using a sync.Pool is a good choice.

If there's no concurrency in this language, then maybe another approach is to have a stack of args. Or maybe one large array

var argstack [N]interface{}

and a "stack pointer" saying where the next free spot is. You'd be trading the cost of allocating arg slices for the up-front cost of a large static allocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment