$30 off During Our Annual Pro Sale. View Details »

Running in Parallel: WebAssembly, Web Workers and Worklets

Running in Parallel: WebAssembly, Web Workers and Worklets

UI is freezing, one of the CPU cores burning, you can do nothing at the moment. The only thing you can hear right now is the howl cooler. The single perception you can feel is hot as a hell metal case of the laptop. This is not a horror story or a thriller – this is a modern web app that runs everything in the main JS thread.

We should use the benefits of parallel computing to make the user feel like in a rainbow paradise instead of a nightmare. We have all the powers to execute the code in parallel and keep the main thread ready to interactions. With modern features like Web Workers, WebAssembly, and Worklets, this is not true anymore. I want to share the experience running jobs in parallel on real examples, that could be adapted to your requirements.

Vitalii Bobrov

November 26, 2019
Tweet

More Decks by Vitalii Bobrov

Other Decks in Programming

Transcript

  1. Running in Parallel
    Web Workers, Worklets and
    WebAssembly

    View Slide

  2. Running in Parallel
    All the Buzz Words,
    I ever heard

    View Slide

  3. Running in Parallel
    The Way to Performance

    View Slide

  4. Vitalii Bobrov
    • Lead SW Engineer @ EPAM
    • Angular Wrocław organizer
    • Performance warrior
    @bobrov1989
    https://bobrov.dev

    View Slide

  5. Topics
    • Parallel Computations
    • Web Workers
    • Worklets
    • Web Assembly & AssemblyScript

    View Slide

  6. Parallel Computations

    View Slide

  7. Border Control

    View Slide

  8. Passport Control
    Border
    Line
    One Thread

    View Slide

  9. Passport Control
    Border
    Line
    One Thread

    View Slide

  10. Passport Control
    Border
    Line
    One Thread

    View Slide

  11. Passport Control
    Border
    Line
    Async

    View Slide

  12. Passport Control
    Border
    Line
    Async

    View Slide

  13. Passport Control
    Border
    Line
    Async

    View Slide

  14. Passport Control
    Border
    Line
    Async

    View Slide

  15. Passport Control
    Border
    Line
    Async

    View Slide

  16. Parallel doesn’t mean
    X times faster

    View Slide

  17. Passport Control
    Border
    Line
    Multi Thread
    Passport Control
    Line

    View Slide

  18. Passport Control
    Border
    Line
    Multi Thread
    Passport Control
    Line

    View Slide

  19. Passport Control
    Border
    Line
    Multi Thread
    Passport Control
    Line

    View Slide

  20. Thread is not equal
    to CPU core

    View Slide

  21. All Passports
    Border
    Line
    Worker / Worklet
    EU Passports
    Line

    View Slide

  22. All Passports
    Border
    Line
    Worker / Worklet
    EU Passports
    Line

    View Slide

  23. All Passports
    Border
    Line
    Worker / Worklet
    EU Passports
    Line

    View Slide

  24. Complicate Code

    View Slide

  25. Web Workers

    View Slide

  26. UI Blocking

    View Slide

  27. Disappointed User

    View Slide

  28. const worker = new Worker('path/to/worker.js');
    Init Web Worker

    View Slide

  29. Full-Duplex Communication
    Main Thread Worker Thread
    worker.postMessage('data');
    worker.addEventListener('message',
    (event: MessageEvent) => {
    console.log(event.data);
    });
    addEventListener('message',
    (event: MessageEvent) => {
    console.log(event.data);
    });
    postMessage('response');

    View Slide

  30. Worker implementation
    addEventListener('message', ({ data }) => {
    data.sort((a, b) => {
    for (let i = 0; i < 10000; i++) {
    const sum = i * Math.random() * 10;
    }
    return a.order - b.order;
    });
    postMessage(data);
    });

    View Slide

  31. Off-thread

    View Slide

  32. Worklets

    View Slide

  33. Available Worklets
    • Paint
    • Animation
    • Layout
    • Audio

    View Slide

  34. Paint API

    View Slide

  35. Half-Duplex Communication
    Main Thread Worklet Thread
    class CirclesPainter {
    static get inputProperties() {
    return [
    '--circles-offset',
    '--circles-count',
    '--circles-opacity'
    ];
    }
    }
    element.style
    .setProperty('--circles-offset', 10);

    View Slide

  36. Half-Duplex Communication
    Main Thread Worklet Thread
    class CirclesPainter {
    static get inputProperties() {
    return [
    '--circles-offset',
    '--circles-count',
    '--circles-opacity'
    ];
    }
    }
    :root {
    --circles-count: 2;
    --circles-offset: 10;
    --circles-opacity: 1;
    }

    View Slide

  37. Create Worklet
    class MyPaint {
    paint(ctx: CanvasRenderingContext2D) {
    ctx.beginPath();
    ctx.arc(x, y, outerRadius, 0, Math.PI * 2, false);
    ctx.arc(x, y, innerRadius, 0, Math.PI * 2, true);
    ctx.fill();
    }
    }
    registerPaint('my-paint', MyPaint);

    View Slide

  38. Load Worklet
    if ('paintWorklet' in CSS) {
    CSS.paintWorklet.addModule('my-paint');
    }

    View Slide

  39. Use Worklet
    .circle {
    background: #999 paint(my-paint);
    }

    View Slide

  40. QR Code

    View Slide

  41. Chart

    View Slide

  42. WebAssembly

    View Slide

  43. WASM - binary instruction
    format for a stack-based VM

    View Slide

  44. WHAT ?!

    View Slide

  45. WTF ?!

    View Slide

  46. WAT
    WebAssembly Text Format

    View Slide

  47. Devs are Good in Naming

    View Slide

  48. Available Options
    • Rust
    • C/C++
    • TypeScript via AssemblyScript

    View Slide

  49. Install
    npm install assemblyscript

    View Slide

  50. Init
    npx asinit .

    View Slide

  51. Build
    npm run asbuild

    View Slide

  52. Basic function
    import 'allocator/tlsf';
    export function add(a: i32, b: i32): i32 {
    return a + b;
    }

    View Slide

  53. Load WASM module
    import { instantiateStreaming, ASUtil } from 'assemblyscript/lib/loader';
    interface MyApi {
    add(a: number, b: number): number;
    }
    const imports: any = {};
    async function main(): void {
    var interop: ASUtil =
    await instantiateStreaming(fetch('../build/untouched.wasm'), imports);
    console.log("The result is:", interop.add(1, 2));
    }

    View Slide

  54. Running in Parallel
    Performance of Heavy Tasks

    View Slide

  55. @bobrov1989
    https://bobrov.dev
    Vitalii Bobrov

    View Slide