aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralandonovan <adonovan@google.com>2020-06-17 14:22:58 -0400
committerGitHub <noreply@github.com>2020-06-17 14:22:58 -0400
commitfd77f1382f3580e9991b1b20a9985b14dd670dda (patch)
tree3539d39096a45ef2cc89e0c11a82526b19167296
parent61b64bc459900eebc728a4e58505ae6f32215e02 (diff)
downloadstarlark-go-fd77f1382f3580e9991b1b20a9985b14dd670dda.tar.gz
Avoid defensive copy in CALL operator when callee is a Starlark function (#281)
This saves 22% of CPU cycles on one call-intensive benchmark.
-rw-r--r--starlark/interp.go13
1 files changed, 11 insertions, 2 deletions
diff --git a/starlark/interp.go b/starlark/interp.go
index 5290730..004763e 100644
--- a/starlark/interp.go
+++ b/starlark/interp.go
@@ -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] {
@@ -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.