GoogleChromeLabs / wasm-bindgen-rayon

An adapter for enabling Rayon-based concurrency on the Web with WebAssembly.
https://github.com/RReverser/wasm-bindgen-rayon
Apache License 2.0
404 stars 35 forks source link

Slightly confused about initialization directions #28

Closed felipellrocha closed 10 months ago

felipellrocha commented 2 years ago

You must instantiate the main JS+Wasm in a dedicated Worker to avoid blocking the main thread - that is, don't mix UI and Rayon code together. Instead, use a library like Comlink or a custom glue code to expose required wasm-bindgen methods to the main thread, and do the UI work from there.

You'll need to invoke [the initThreadPool function] it right after instantiating your module on the main thread in order to prepare the threadpool before calling into actual library functions:

Which is it? :)

RReverser commented 2 years ago

See https://github.com/GoogleChromeLabs/wasm-bindgen-rayon/issues/18#issuecomment-960949808 for deeper explanation:

Right, the wording there could certainly be improved. It's hard to convey difference between "main thread" as in "main thread from PoV of Rust code" vs "main thread" as in "browser main thread used for UI work".

What that comment meant is that init & initThreadPool need to be invoked from main thread from Rust PoV, even though it actually lives in the worker as per caveats.

It might be helpful to also look at the demo code which invokes those functions in a worker file: https://github.com/GoogleChromeLabs/wasm-bindgen-rayon/blob/5f54573052ea70986e1d23d3ebe876702c671335/demo/wasm-worker.js#L31-L32

I admit it's confusing and open to better rewording PRs.

ceikit commented 2 years ago

See https://github.com/GoogleChromeLabs/wasm-bindgen-rayon/issues/18#issuecomment-960949808 for deeper explanation:

@RReverser for me this is not working :( Js fails on await multiThread.initThreadPool(navigator.hardwareConcurrency); with

Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Worker': #<Memory> could not be cloned.
RReverser commented 2 years ago

Did you build with all required flags from README? Most likely, you're missing +atomics and company, as that error indicates a non-shared memory.

ceikit commented 2 years ago

@RReverser I run

RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' && rustup run nightly-2022-03-14 wasm-pack build --target web --out-dir pkg

and build the pkg successfully but after installing it I get the same issue.

Changing to nightly-2022-04-07 does not help

RReverser commented 2 years ago

That is weird. Are you running in a browser that supports Wasm threads? (e.g. if you're on older Safari, it might be not available)

ceikit commented 2 years ago

@RReverser

For context, I've implemented a genetic algorithm in Rust and would like to run it in wasm on the browser. This works well without multithread, however as the computation it's fairly expensive I've used Rayon for parallelism (that's were wasm-bindgen-rayon comes in play.

Running on Chrome.

This is my wasm-worker.js

import { threads } from 'wasm-feature-detect';
import * as Comlink from 'comlink';

// Wrap wasm-bindgen exports (the `run_ga_wasm` function) to add time measurement.
function wrapExports({ run_ga_wasm }) {
    return ({ run_data, data }) => {
        console.log("Nsdsadsadsadasdasdsa");
        const start = performance.now();
        const dd = run_ga_wasm(run_data, data);
        const time = performance.now() - start;
        return {
            // Little perf boost to transfer data to the main thread w/o copying.
            dd: Comlink.transfer(dd, [dd.buffer]),
            time
        };
    };
}

async function initHandlers() {
    const startThread = async () => {
        const multiThread = await import('fantaga');
        await multiThread.default();
        console.log("NN", navigator.hardwareConcurrency);
        await multiThread.initThreadPool(navigator.hardwareConcurrency);  <--- fails here 
        return wrapExports(multiThread);
    }

    let multiThreadStarted = await startThread();

    return Comlink.proxy({
        multiThreadStarted
      });
    // return Comlink.proxy( multiThreadStarted );
}

Comlink.expose({
    handlers: initHandlers()
});
ceikit commented 2 years ago

It's called in App.tsx like this :

[...]
import * as Comlink from 'comlink';

function App(this: any) {

  const [sss, setCard] = useState<Number>();
  const [wasm_init, setWasmInit] = useState<number>();
  const [team, setTeam] = useState<Array<Player>>();
  /// need to 'start' the wasm module with init()
  /// it is async so we need to wait 
  useEffect(() => {
    const fetchData = async () => {

      let handlers = await Comlink.wrap(
        new Worker(new URL('./wasm-worker.js', import.meta.url), {
          type: 'module'
        })
      );

      console.log(handlers);

      setCard(3);
    };

    fetchData();

  }, [])

[...]

Also I noticed I cannot do . handlers (as in the example) on the above Comlink.wrap, I can just do

image

not sure that's an issue

RReverser commented 10 months ago

Latest version of wasm-bindgen-rayon allows to use Rayon directly from the main thread, without extra Worker and Comlink.