Skip to content

Commit c67165d

Browse files
committed
syscall/js: Cache arg slices to avoid heap allocation (see golang#39740)
1 parent b235317 commit c67165d

File tree

1 file changed

+29
-7
lines changed

1 file changed

+29
-7
lines changed

src/syscall/js/js.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -358,15 +358,37 @@ func (v Value) SetIndex(i int, x interface{}) {
358358

359359
func valueSetIndex(v ref, i int, x ref)
360360

361-
func makeArgs(args []interface{}) ([]Value, []ref) {
362-
argVals := make([]Value, len(args))
363-
argRefs := make([]ref, len(args))
361+
const pooledArgsLength = 16
362+
363+
var (
364+
argRefsPool [][]ref
365+
argValsPool [][]Value
366+
)
367+
368+
func makeArgs(args []interface{}) (argVals []Value, argRefs []ref) {
369+
if len(args) <= pooledArgsLength && len(argRefsPool) > 0 {
370+
last := len(argValsPool) - 1
371+
argVals = argValsPool[last]
372+
argRefs = argRefsPool[last]
373+
argValsPool = argValsPool[:last]
374+
argRefsPool = argRefsPool[:last]
375+
} else {
376+
argVals = make([]Value, pooledArgsLength)
377+
argRefs = make([]ref, pooledArgsLength)
378+
}
379+
argVals, argRefs = argVals[:len(args)], argRefs[:len(args)]
364380
for i, arg := range args {
365381
v := ValueOf(arg)
366382
argVals[i] = v
367383
argRefs[i] = v.ref
368384
}
369-
return argVals, argRefs
385+
return
386+
}
387+
388+
func poolArgs(argVals []Value, argRefs []ref) {
389+
// No need to check length, because larger slices are compatible
390+
argValsPool = append(argValsPool, argVals)
391+
argRefsPool = append(argRefsPool, argRefs)
370392
}
371393

372394
// Length returns the JavaScript property "length" of v.
@@ -389,7 +411,7 @@ func (v Value) Call(m string, args ...interface{}) Value {
389411
argVals, argRefs := makeArgs(args)
390412
res, ok := valueCall(v.ref, m, argRefs)
391413
runtime.KeepAlive(v)
392-
runtime.KeepAlive(argVals)
414+
poolArgs(argVals, argRefs)
393415
if !ok {
394416
if vType := v.Type(); !vType.isObject() { // check here to avoid overhead in success case
395417
panic(&ValueError{"Value.Call", vType})
@@ -411,7 +433,7 @@ func (v Value) Invoke(args ...interface{}) Value {
411433
argVals, argRefs := makeArgs(args)
412434
res, ok := valueInvoke(v.ref, argRefs)
413435
runtime.KeepAlive(v)
414-
runtime.KeepAlive(argVals)
436+
poolArgs(argVals, argRefs)
415437
if !ok {
416438
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
417439
panic(&ValueError{"Value.Invoke", vType})
@@ -430,7 +452,7 @@ func (v Value) New(args ...interface{}) Value {
430452
argVals, argRefs := makeArgs(args)
431453
res, ok := valueNew(v.ref, argRefs)
432454
runtime.KeepAlive(v)
433-
runtime.KeepAlive(argVals)
455+
poolArgs(argVals, argRefs)
434456
if !ok {
435457
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
436458
panic(&ValueError{"Value.Invoke", vType})

0 commit comments

Comments
 (0)