Skip to content

syscall/js: huge copy overhead when passing byte slices over certain Go/JS boundaries #26193

Closed
@leidegre

Description

@leidegre

This related to my effort to get Go WASM/JS to work with a WebWorker process.

What version of Go are you using (go version)?

go version devel +0e0cd70ecf Tue Jul 3 04:16:23 2018 +0000 windows/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

set GOARCH=wasm
set GOBIN=
set GOCACHE=C:\Users\leidegre\AppData\Local\go-build
set GOEXE=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=js
set GOPATH=C:\Users\leidegre\go
set GOPROXY=
set GORACE=
set GOROOT=C:\Users\leidegre\Source\go
set GOTMPDIR=
set GOTOOLDIR=C:\Users\leidegre\Source\go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=0
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-fPIC -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\leidegre\AppData\Local\Temp\go-build097448038=/tmp/go-build -gno-record-gcc-switches
set VGOMODROOT=

What did you do?

Trying to pass an nil []byte slice from a WebWorker process to a web application.

What did you expect to see?

That gigabytes of memory is not allocated.

What did you see instead?

Gigabytes of memory is being allocated on each postMessage call. This crashes both Chrome and Firefox render process. Haven't tested Edge.

Additional findings

as soon as you console.log or pass a slice through a boundary context, for example postMessage there's copying associated with the ArrayBuffer. For some reason, the gigabyte ArrayBuffer (Go linear memory) gets copied as well and the heap just explodes typically grinding the web page to a halt or crashing the page.

I was able to work around this issue by passing the slice through this helper function first.

[worker.js]

function slice({ byteOffset, byteLength, buffer }) {
  return buffer.slice(byteOffset, byteOffset + byteLength)
}

[worker.go]

var x []byte
x = append(x, 2)
x = append(x, 3)
x = append(x, 5)
y := js.TypedArrayOf(x)
z := global.Call("slice", y)
y.Release()

global.Call("postMessage", z) // from WebWorker postMessage

Note that problem occur even if the slice is empty or nil. If worker.js, i.e. the WebWorker process does console.log at any time on any object that is backed by the ArrayBuffer you'll see crazy heap explosion and subsequent crashes. Not sure what to do about this but I thought I'd share my findings here. It's mostly a side effect of how things are not something Go/WASM is doing wrong but maybe this can be improved somehow? Passing values between Go/JS seems like a natural thing to do.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions