aboutsummaryrefslogtreecommitdiff
path: root/src/syscall/js/js_test.go
diff options
context:
space:
mode:
authorJacob <vattlabs@gmail.com>2024-04-18 16:24:24 +0000
committerCherry Mui <cherryyz@google.com>2024-04-19 14:35:26 +0000
commit104c293ffe0037de2462cdd404a6910dcf58298d (patch)
tree8b4aaf686ef8de7bb38e165d0ec3cd4c4b9a91a2 /src/syscall/js/js_test.go
parentf31fcc75385bc8037b46aea7f05022520d8c8148 (diff)
downloadgo-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.go74
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.