These are some notes from some digging I did yesterday.
What I was trying to find out how much copying of buffers is involved with a WASM module interacting with IndexedDB or the network (via fetch).
Anyway, below are my unedited notes.
WASM having its own bunch of memory in the browser means there may be more copying involved.
In JS, you can just access part of rust memory.
You get back a pointer from the WASM into its own memory. You can then construct a Uint8Array
from that pointer, length & the ArrayBuffer
that is backing WASM’s memory.
This will not involve a copy. If the WASM side continues changing the array, the values update accordingly in the Uint8Array
, because it actually accesses the backing ArrayBuffer
.
This can be used to e.g. speed up storing Uint8Array
s in the IndexedDB object store (IDBObjectStore.put() - Web APIs | MDN), if you just use that Uint8Array
. IndexedDB will still do another copy, but that one you won’t get around, since it happens via JS <-> IndexedDB too.
Unfortunately IndexedDB doesn’t support getting data off disk directly into a pre-existing ArrayBuffer
this way. the IDBObjectStore’s get
method will always create new ArrayBuffer
s, and you need to copy the data over from there to WASM. This copy wouldn’t be necessary in JS since you can work with it there directly.
What is (maybe?) possible is sacrifice the zero-copy-on-write and instead gain zero-copy-on-read via storing Blob
s in IndexedDB instead of storing Uint8Array
s, and then you can blob.stream().getReader({ type: "byob" }).read(new Uint8Array(wasmMemory, offset, length))
.
A good example for an API that does support this zero-copy fetching is crypto.getRandomValues
. Its first an only parameter is the Uint8Array
which is to be populated with random values.
This makes it possible to just pass it some WASM memory to initialize random bytes into.
More APIs that allow zero-copy into WASM are fetch
requests: It’s possible to read the data stream directly into WASM memory via response.body.getReader().getReader({ type: "byob" })
and then using the resulting ReadableStreamBYOBReader - Web APIs | MDN and its read
method with a Uint8Array
parameter to write to.
Similarily to IndexedDB, when receiving messages via WebSockets, binary data can be recieved as Blob
s, and again, one can call .stream().getReader({ type: "byob" }).read(new Uint8Array(wasmMemory, pointer, length))
on it to transfer it into wasm memory directly.
Sending messages via WebSockets can be done zero-copy easily, too, simply via calling someWebSocket.send(new Uint8Array(wasmMemory, pointer, length))
.
Unfortunately the BYOB reader (ReadableStreamBYOBReader
) is not implemented in Safari.