WASM (Web Assembly) allows us to compile all sorts of language (ideally ones without a GC) into an assembly that can be executed by any standard browser.
This is cool because that way we arenât doomed to write javascript and its decendents until eternity.
To be fair, WASM doesnât get you much of a performance increase compared to very carefully written JS.
It also doesnât give you access to any new browser APIs.
The advantages really boil down to the fact that you can use a better language than JS for a web app.
I specifically tried out Rust with WASM using wasm-bindgen.
Having a solution at my hands, I searched for a problem to solve.
I decided to write a basic image manipulation web app.
I ended up not writing very performant code.
This is when I discovered the performance profiler tool.
Figure 1. Performance of moving a layer in the image manipulation app (various performance improvements from left to right)
One example where the performance tool helped me is the performance of moving layers displayed in the first figure.
Luckily, the stack trace is as detailed as it gets:
The mouse movement event ends up calling various javascript functions (handleMove
, extend
, extend_step
).
After that there is a "js-to-wasm" layer. From here on, we enter functions written in Rust.
As you can see, most time is needed for propagating changes (engine::utils::propagate_changes_up
) which seems to be done with a WebGL blender.
This WebGL blender then calls functions from web-sys
which just enters a "wasm-to-js" layer and calls native javascript API again to do its job.
From the second to the third screenshot, I realized that is needed for texImage2D
which loads an image into a WebGL texture.
This is dumb because the images that are loaded donât change when the layers are moved. Loading them once at the start is enough.
From the third to the last screenshot, I changed the way that the result is displayed in the browser which again removed one indirection.
In the first screenshot, moving a layer takes up to 5 frames with one frame being ~16ms long!
This means that the app will only render roughly 12 frames per second.
However, in the last screenshot, it doesnât take a full frame for the calculation.
The app can run at the full 60 frames per second.
Figure 2. Performance of drawing on a layer (big improvement from left to right)
Another example of the usage of the performance tool is the second figure.
I realized that most time is used creating the image buffer of the brush itself which doesnât change at all when drawing in this case.
TL;DR I investigate my beginner mistakes with basic browser tooling