diff options
author | Jacob <vattlabs@gmail.com> | 2024-04-18 16:24:24 +0000 |
---|---|---|
committer | Cherry Mui <cherryyz@google.com> | 2024-04-19 14:35:26 +0000 |
commit | 104c293ffe0037de2462cdd404a6910dcf58298d (patch) | |
tree | 8b4aaf686ef8de7bb38e165d0ec3cd4c4b9a91a2 /src/syscall/js/js_test.go | |
parent | f31fcc75385bc8037b46aea7f05022520d8c8148 (diff) | |
download | go-104c293ffe0037de2462cdd404a6910dcf58298d.tar.gz go-104c293ffe0037de2462cdd404a6910dcf58298d.zip |
syscall/js: allocate arg slices on stack for small numbers of args
The existing implementation causes unnecessary heap allocations for
javascript syscalls: Call, Invoke, and New. The new change seeks to
hint the Go compiler to allocate arg slices with length <=16 to the
stack.
Original Work: CL 367045
- Calling a JavaScript function with 16 arguments or fewer will not
induce two additional heap allocations, at least with the current Go
compiler.
- Using syscall/js features with slices and strings of
statically-known length will not cause them to be escaped to the heap,
at least with the current Go compiler.
- The reduction in allocations has the additional benefit that the
garbage collector runs less often, blocking WebAssembly's one and only
thread less often.
Fixes #39740
Change-Id: I815047e1d4f8ada796318e2064d38d3e63f73098
GitHub-Last-Rev: 36df1b33a4506e216767d8a73395f2fafdd80eba
GitHub-Pull-Request: golang/go#66684
Reviewed-on: https://go-review.googlesource.com/c/go/+/576575
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/syscall/js/js_test.go')
-rw-r--r-- | src/syscall/js/js_test.go | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 8823421b89..cc809ac107 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -581,6 +581,80 @@ func TestGarbageCollection(t *testing.T) { } } +// This table is used for allocation tests. We expect a specific allocation +// behavior to be seen, depending on the number of arguments applied to various +// JavaScript functions. +// Note: All JavaScript functions return a JavaScript array, which will cause +// one allocation to be created to track the Value.gcPtr for the Value finalizer. +var allocTests = []struct { + argLen int // The number of arguments to use for the syscall + expected int // The expected number of allocations +}{ + // For less than or equal to 16 arguments, we expect 1 alloction: + // - makeValue new(ref) + {0, 1}, + {2, 1}, + {15, 1}, + {16, 1}, + // For greater than 16 arguments, we expect 3 alloction: + // - makeValue: new(ref) + // - makeArgSlices: argVals = make([]Value, size) + // - makeArgSlices: argRefs = make([]ref, size) + {17, 3}, + {32, 3}, + {42, 3}, +} + +// TestCallAllocations ensures the correct allocation profile for Value.Call +func TestCallAllocations(t *testing.T) { + for _, test := range allocTests { + args := make([]any, test.argLen) + + tmpArray := js.Global().Get("Array").New(0) + numAllocs := testing.AllocsPerRun(100, func() { + tmpArray.Call("concat", args...) + }); + + if numAllocs != float64(test.expected) { + t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected) + } + } +} + +// TestInvokeAllocations ensures the correct allocation profile for Value.Invoke +func TestInvokeAllocations(t *testing.T) { + for _, test := range allocTests { + args := make([]any, test.argLen) + + tmpArray := js.Global().Get("Array").New(0) + concatFunc := tmpArray.Get("concat").Call("bind", tmpArray) + numAllocs := testing.AllocsPerRun(100, func() { + concatFunc.Invoke(args...) + }); + + if numAllocs != float64(test.expected) { + t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected) + } + } +} + +// TestNewAllocations ensures the correct allocation profile for Value.New +func TestNewAllocations(t *testing.T) { + arrayConstructor := js.Global().Get("Array") + + for _, test := range allocTests { + args := make([]any, test.argLen) + + numAllocs := testing.AllocsPerRun(100, func() { + arrayConstructor.New(args...) + }); + + if numAllocs != float64(test.expected) { + t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected) + } + } +} + // BenchmarkDOM is a simple benchmark which emulates a webapp making DOM operations. // It creates a div, and sets its id. Then searches by that id and sets some data. // Finally it removes that div. |