Skip to content

Commit

Permalink
Avoid defensive copy in CALL operator when callee is a Starlark funct…
Browse files Browse the repository at this point in the history
…ion (google#281)

This saves 22% of CPU cycles on one call-intensive benchmark.
  • Loading branch information
adonovan authored Jun 17, 2020
1 parent 61b64bc commit fd77f13
Showing 1 changed file with 11 additions and 2 deletions.
13 changes: 11 additions & 2 deletions starlark/interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const vmdebug = false // TODO(adonovan): use a bitfield of specific kinds of err
// - opt: record MaxIterStack during compilation and preallocate the stack.

func (fn *Function) CallInternal(thread *Thread, args Tuple, kwargs []Tuple) (Value, error) {
// Postcondition: args is not mutated. This is stricter than required by Callable,
// but allows CALL to avoid a copy.

if !resolve.AllowRecursion {
// detect recursion
for _, fr := range thread.stack[:len(thread.stack)-1] {
Expand Down Expand Up @@ -276,9 +279,15 @@ loop:
// positional args
var positional Tuple
if npos := int(arg >> 8); npos > 0 {
positional = make(Tuple, npos)
positional = stack[sp-npos : sp]
sp -= npos
copy(positional, stack[sp:])

// Copy positional arguments into a new array,
// unless the callee is another Starlark function,
// in which case it can be trusted not to mutate them.
if _, ok := stack[sp-1].(*Function); !ok || args != nil {
positional = append(Tuple(nil), positional...)
}
}
if args != nil {
// Add elements from *args sequence.
Expand Down

0 comments on commit fd77f13

Please sign in to comment.