Is there a performance gain to use web-sys instead of plain old JavaScript?

Viewed 1504

I used wasm-bindgen to write a very basic JS/Wasm webapp in which a grayscale transformation (written in Rust) is applied to an image uploaded by the user of the webapp through an <input type="file"> and displayed in a <canvas>.

Doing so, I had to load the image in the WebAssembly memory space, apply the transformation on each pixel then return the result in the memory space for the JS to take care of the display:

import { memory} from "img-grayscale-wasm/img_grayscale_wasm_bg";
import {MyImage} from "img-grayscale-wasm"

//...

async function processImg(file, width, height){
  const canvas = document.getElementById("pixel-grayscale");

  var resp = await fetch(file.name)
  var bytes = await resp.arrayBuffer()

  const myImage = MyImage.new();
  const ptr = myImage.alloc(bytes.byteLength); 

  // Copy to memory space
  const imgArray = new Uint8Array(memory.buffer, ptr, bytes.byteLength);    
  imgArray.set(new Uint8Array(bytes));

  // transform img
  myImage.read_img(ptr, bytes.byteLength) 
  const grayScalePtr = myImage.to_grayscale(width, height)

  // read from memory space
  const arr = new Uint8ClampedArray(memory.buffer, grayScalePtr, width * height * 4);
  let imageData = new ImageData(arr, width);

  getContextFromCanvas(canvas, width, height).putImageData(imageData, 0, 0);
}

I think I can avoid the whole process of copying stuff back and forth to the memory space if I use the web's API binding defined in the web-sys crate, from fetching the file to the display of the grayscaled picture.

This is where I get confused: performance-wise, is there a gain in using web-sys and its web's API bindings instead of doing it all in JavaScript? Well, I suppose there is, but where is it?

1 Answers

If I understand you correctly, you're marshalling the data back and forth using browser methods at the moment to bask in web-sys's glory. I have some good news and some bad news for you.

The good news is that if your code is laid out the way you've said it is (and this is where a snippet would've come in very handy), you'll gain on one memory copy going each way.

Right now, the process would be as follows:

            1              2
         ======>        =====>       \
Browser         WebWorker      Rust   ||
         <======        <=====      <=/
            4              3

(Quality ASCII art, I know)

1 is a user input of some sort. 2->3 is a fetch cycle. 4 is the marshalling back.

At best, if your code is indeed structured this way, you can get rid of the data copy on steps 2 and 3 by moving everything to web-sys. You will not get rid of the (relatively minimal) copy of data on 1, nor on 4.

Depending on the size of the image you're desaturating, and the computer you're running it on, this may or may not present a gain in performance. I have not looked into the internals of web-sys (this isn't typically my area of expertise - I'm an embedded systems developer), so I cannot speak about the internals of the crate itself, but I assume fetch() as done by the browser or done through web-sys to be identical in performance.

Related