Instead of blocking the main browser thread with synchronous calls to the WASM API, everything runs in a web worker now. Using web workers directly is a painful API, so using https://github.com/GoogleChromeLabs/comlink to make it work like normal async calls.
Benefits:
not blocking the main thread
easier to swap out the backend with something potentially remote
enables the progress updates below
There's extra boilerplate to wrap all the WASM APIs in the web worker, but it's also an opportunity to make cleaner TS APIs for them
Problems:
Per the docs, the messages to/from web workers are copied. That's potentially lots of overhead. I haven't yet benchmarked the e2e times for loading a big map (shipping all the OSM data to the web worker) or rendering. For rendering, it's starting to get a little excessive -- on the Rust side, do work to form GeoJSON, serialize as a JSON string, copy the big string to the main thread, parse the JSON string, then hand to maplibre, which again copies to web workers for rendering.
Every API call becoming async makes some Svelte code messier. I'll leave some comments on the code; maybe there are nicer ways to handle those cases.
Plumbing progress updates
By passing a JS callback to an expensive WASM call and invoking that callback repeatedly while doing Rust stuff, the frontend can get progress updates. And since the expensive work is happening in a web worker now, the main browser thread can render those messages. The style of the loading screen can of course be greatly improved.
Timer Rust API and flame-graphs
This is a more general concern for Rust code that does multiple expensive steps. It's very useful to print progress updates, and at the end, to summarize where most time was spent. This is an example output tree from A/B Street without much nesting:
I set up a second variation of this in SPC that uses tracing. Output looks like this:
and apparently I wrote https://alan-turing-institute.github.io/uatk-spc/advanced_code_walkthrough.html#logging-with-structure about it.
This PR has yet another simple start at a timer.rs. Ultimately I'd like to hook into a proper flame-graph UI to explore the results in a nicer way. Also uncertain how the Rust API for starting/stopping sections or counting iterations through long loops ought to look.
Demo: Screencast from 12-05-24 13:30:13.webm
There are 3 things I'd like feedback on, all possibly a reusable pattern to follow in the UATP templates.
@stuartlynn, @yongrenjie, @sgreenbury, @andrewphilipsmith
Running the WASM backend in a web worker
Instead of blocking the main browser thread with synchronous calls to the WASM API, everything runs in a web worker now. Using web workers directly is a painful API, so using https://github.com/GoogleChromeLabs/comlink to make it work like normal async calls.
Benefits:
Problems:
Plumbing progress updates
By passing a JS callback to an expensive WASM call and invoking that callback repeatedly while doing Rust stuff, the frontend can get progress updates. And since the expensive work is happening in a web worker now, the main browser thread can render those messages. The style of the loading screen can of course be greatly improved.
Timer Rust API and flame-graphs
This is a more general concern for Rust code that does multiple expensive steps. It's very useful to print progress updates, and at the end, to summarize where most time was spent. This is an example output tree from A/B Street without much nesting: I set up a second variation of this in SPC that uses
tracing
. Output looks like this: and apparently I wrote https://alan-turing-institute.github.io/uatk-spc/advanced_code_walkthrough.html#logging-with-structure about it.This PR has yet another simple start at a
timer.rs
. Ultimately I'd like to hook into a proper flame-graph UI to explore the results in a nicer way. Also uncertain how the Rust API for starting/stopping sections or counting iterations through long loops ought to look.