Multithreading

Takeaways:

  • Performance can be achieved by parallelizing tasks.
  • Native: Tasks submitted to the thread pool are executed immediately[citation needed].
  • WASM: Tasks submitted to the thread pool are executed when control returns to the browser.
  • WASM: Since tasks will never run until the main thread returns, the main thread cannot wait for tasks to complete.
  • Github issue(s): amethyst#2191

Amethyst uses rayon to manage a thread pool, and parallel processing is achieved by submitting tasks to that pool.

Dispatcher

Native

In native applications, parallel execution is enabled by default.

When tasks are submitted to the thread pool, they are executed immediately.

Parallel execution control flow

 

sequenceDiagram
    participant main
    participant #nbsp;
    participant W0
    participant W1
    participant W2

    main ->>+ #nbsp;: dispatch_par()

    rect rgba(0, 100, 255, .2)
        #nbsp; -->>+ W2: 🎶 Play Music#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;

        #nbsp; -->>+ W0: 🎮 Device#nbsp;#nbsp;#nbsp;#nbsp;
        #nbsp; -->>+ W1: 🌐 Network #nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;

        W0 -->>- #nbsp;: #nbsp;
        W1 -->>- #nbsp;: #nbsp;

        W2 -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ W2: 📦 Load Assets#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;
        Note left of W2: 📦 Task

        #nbsp; -->>+ W1: 📄 Read File#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;#nbsp;

        W2 -->>- #nbsp;: #nbsp;
        W1 -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ W0: 📡 Position#nbsp;#nbsp;#nbsp;
        W0 -->>- #nbsp;: #nbsp;

        W1 -->+ W1: 📦 Asset Load Task

    end

    #nbsp; ->>- main: #nbsp;


    %% Asset Load Task end
    W1 -->- W1: #nbsp;

    main ->>+ #nbsp;: dispatch_thread_local()

    rect rgba(180, 0, 220, .2)
        #nbsp; -->>+ main: 🖌️ Render
        main -->>- #nbsp;: #nbsp;
        #nbsp; -->>+ main: 🎨 Load Textures
        main -->>- #nbsp;: #nbsp;
    end

    #nbsp; ->>- main: #nbsp;

WASM

When adding WASM support, sequential execution is used.

If we tried to use parallel execution, tasks that are submitted to the thread pool are queued. They will only run when control has been returned to the browser, as the browser will only send the tasks (as messages) to each web worker when it has control. This is a problem because the main thread would be waiting for all the tasks to complete, when in fact nothing is running.

Sequential execution control flow

 

sequenceDiagram
    participant main
    participant #nbsp;
    participant W0;
    participant W1;
    participant W2;

    main ->>+ #nbsp;: dispatch_seq()

    rect rgba(0, 100, 255, .2)
        #nbsp; -->>+ main: 🎮 Device
        main -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ main: 🌐 Network
        main -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ main: 📦 Load Assets
        Note left of #nbsp;: 📦 Task
        main -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ main: 📄 Read File
        main -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ main: 📡 Position
        main -->>- #nbsp;: #nbsp;

        #nbsp; -->>+ main: 🎶 Play Music
        main -->>- #nbsp;: #nbsp;
    end

    #nbsp; ->>- main: #nbsp;

    main ->>+ #nbsp;: dispatch_thread_local()

    rect rgba(180, 0, 220, .2)
        #nbsp; -->>+ main: 🖌️ Render
        main -->>- #nbsp;: #nbsp;
        #nbsp; -->>+ main: 🎨 Load Textures
        main -->>- #nbsp;: #nbsp;
    end

    #nbsp; ->>- main: #nbsp;

    W0 -->+ W0: 📦 Asset Load Task

    %% Asset Load Task end
    W0 -->- W0: #nbsp;