Parallel Computing

 

 

  

mhadaily

What will be explored

Web Workers
Web Assembly
Service Worker
Parallel Computing VS Concurrency

mhadaily

Worklets

mhadaily

What will be explored

Web Workers
Web Assembly
Service Worker
Parallel Computing VS Concurrency

mhadaily

Worklets

PRACTICAL

<ME />

import HelloWorld from "@/components/HelloWorld.component";
export default {
  name: "whoAmI",
  data: {
      me: {
        name: "Majid Hajian",
        location: "Oslo, Norway",
        description: ```
        	Passionate Software engineer, 
	        Community Leader, Author and international Speaker
         ```,
        main: "Web, Javascripter, Flutter/Dart, IoT",
        homepage: "https://www.majidhajian.com",
        socials: {
          twitter: "https://www.twitter.com/mhadaily",
          github: "https://www.github.com/mhadaily"
        },
        books: {
          "Progressive Web App with Angular": {
             version: "1.0.0", 
             publishedBy: "Apress", 
             foundOn: "www.pwawithangular.com",
          }
        },
        author: {
          packtpub: "PWA development, 7 hours video course",
          Udemy: "PWA development, 7 hours video course"
        }
        founder: "Softiware As (www.Softiware.com)"
        devDependencies: {
          tea: "green", mac: "10.14+",
        },
        engines: {
          VueJsOslo: "Orginizer", MobileEraConference: "Orginizer",
          ngVikingsConference: "Orginizer", 
          MobileMeetupOslo:"Co-Orginizer",AngularOslo: "Co-Orginizer",
          FlutterDartOslo: "Co-Orginizer",Framsia: "Co-Orginizer",          
        }};} 
     };

mhadaily

Find me on the internet by

Parallel Computing

Concurrency

mhadaily

mhadaily

Thread is not equal to CPU core

mhadaily

mhadaily

Javascript code

mouse event

other event

other event

Event queue

mhadaily

mhadaily

Javascript code

mouse event

Some other event

other event

Web Workers

mhadaily

mhadaily

Spwan another instance of the JS Engine

Runs in worker thread

No shared state

Communicate vis msg

Have their own event(msg) queue

No access to dom & window obj

Javascript code

msg

msg

mouse event

other event

some other  event

Browser Event Queue

Web Worker Message Queue

mhadaily

mhadaily

Javascript code

msg

msg

mouse event

other event

some other  event

Browser Event Queue

Web Worker Message Queue

mhadaily

mhadaily

Concurrency

Task Parallelism?

Javascript code

msg

msg

mouse event

other event

some other  event

Browser Event Queue

Web Worker Message Queue

Parallel

Core 2

Core 1

mhadaily

mhadaily

Use cases

  • Data and web page caching
  • Image manipulation and encoding (base64 conversion)
  • Canvas drawing and image filtering
  • Network polling and web sockets
  • Background I/O operations
  • Video/Audio buffering and analysis
  • Virtual DOM diffing
  • Local database (indexedDB) operations
  • Computationally intensive data operations

mhadaily

mhadaily

postMessage

Main Thread

Worker Thread

mhadaily

mhadaily

// Main
const worker = new Worker("./worker.js");
let counter = 0;
setInterval(() => {
  counter = counter + 1;
  worker.postMessage({ action: "power2", payload: counter });
}, 3000);

mhadaily

mhadaily

// Worker
self.onmessage = message => {
  const { data } = message;
  switch (data.action) {
    case "power2":
      const number = data.payload;
      const power2 = number * number;
      self.postMessage({ command: "power2", payload: power2 });
      break;
  }
};

mhadaily

mhadaily

// Main
const worker = new Worker("./worker.js");
let counter = 0;
setInterval(() => {
  counter = counter + 1;
  worker.postMessage({ action: "power2", payload: counter });
}, 1000);

worker.onmessage = message => {
  const { data } = message;
  switch (data.command) {
    case "power2":
      const power2 = data.value;
      console.log("power2: ", power2);
      break;
  }
};

mhadaily

mhadaily

Main Thread

Worker Thread

function  power2(){}

w.power2(5);

const w = new Worker();

mhadaily

mhadaily

mhadaily

mhadaily

// Main 
import * as Comlink from "https://unpkg.com/comlink@alpha/dist/esm/comlink.mjs";
// import * as Comlink from "../../../dist/esm/comlink.mjs";

async function init() {
  const worker = new Worker("./worker.js");
  const service = Comlink.proxy(worker);
  const power2 = await service.power2(2);
  console.log(power2);
  const increment = await service.increment(2);
  console.log(increment);
}

init();

mhadaily

mhadaily

// Worker
importScripts("https://unpkg.com/comlink@alpha/dist/umd/comlink.js");
// importScripts("../../../dist/umd/comlink.js");

const service = {
  power2: value => value * value,
  increment: (value) => value * 1
};

Comlink.expose(service);

mhadaily

mhadaily

// all imports 
class UsersWrapper extends Component {
    worker;
    workerService;
  
    constructor(props) {
        super(props);
        this.state = {
            users: [],
        };
    }
    componentDidMount() {
        this.worker = new Worker('./worker.js');
      
        this.workerService = Comlink.wrap(worker);
      
        // Assumption is we have a list of 3000 users! 
        fetchUsers().then(users => {
            this.setState({ users });
        })
    }
    async sortAscending() {
        const sortedUsers = await this.workerService.sort(this.state.users);
		this.setState({ users: sortedUsers });
        return;
    }
   componentDidMount(){
     this.worker.terminate();
   }
   render() {
      return this.state.users.slice(0,20).map((user, index) => (<User key={user.id}>);
   }
}

mhadaily

mhadaily

// WORKER 
// all importScripts();

const service = {
  sortDesc(users){},
  sortAsc(users) {
        for (let i = 0; i < users.length-1; i++) {
            for (let j = i+1; j < users.length; j++) {
                if (users[i].commentCount > users[j].commentCount) {
                    const t = users[i];
                    users[i] = users[j];
                    users[j] = t;
                }
            }
        }
  }
};

Comlink.expose(service);

mhadaily

mhadaily

Web Assembly

Different languages

 

and

 

 run in browsers.

mhadaily

mhadaily

how about Javascript / web developer?

AssemblyScript compiles a strict subset of TypeScript to WebAssembly 

npm init
npm install assemblyscript/assemblyscript
npx asinit .


/*
  ./assembly
  Directory holding the AssemblyScript sources being compiled to WebAssembly.

  ./assembly/tsconfig.json
  TypeScript configuration inheriting recommended AssemblyScript settings.

  ./assembly/index.ts
  Exemplary entry file being compiled to WebAssembly to get you started.

  ./build
  Build artifact directory where compiled WebAssembly files are stored.

  ./build/.gitignore
  Git configuration that excludes compiled binaries from source control.

  ./index.js
  Main file loading the WebAssembly module and exporting its exports.

  ./package.json
  Package info containing the necessary commands to compile to WebAssembly.
*/

mhadaily

mhadaily

// The entry file of your WebAssembly module.
export function power2(a: i32): i32 {
  return a + a;
}

// no optimization, no memoization
export function fibonacci(num: i32): i32 {
  if (num <= 1) return 1;

  return fibonacci(num - 1) + fibonacci(num - 2);
}

mhadaily

mhadaily

var int8: i8 = <i8>0; // 8-bit signed integer [-128 to 127]
var uint8: u8 = <u8>0; // 8-bit unsigned integer [0 to 255]
var int16: i16 = <i16>0; // 16-bit signed integer [-32,768 to 32,767]
var uint16: u16 = <u16>0; // 16-bit unsigned integer [0 to 65,535]
var int32: i32 = <i32>0; // 32-bit signed integer [-2,147,483,648 to 2,147,483,647]
var uint32: u32 = <u32>0; // 32-bit unsigned integer [0 to 4,294,967,295]
var int64: i64 = <i64>; // 64-bit signed integer [-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807]
var uint64: i64 = <u64>0; // 64-bit unsigned integer [0 to 18,446,744,073,709,551,615]
var float32: f32 = <f32>0.0; // 32-bit float [32 bit float range]
var float64: f64 = <f64>0.0; // 64-bit float [64 bit float range]
var pointer: usize = <usize>0; // a 32/64-bit pointer to a location in memory

mhadaily

mhadaily

import "allocator/tlsf";

let ptr = memory.allocate(64);

// do something with ptr ...
// 
// ... once you are done, make sure you free it again. 
memory.free(ptr);

mhadaily

mhadaily

npm run asbuild


/**
Created: ./build/optimized.wasm
Created: ./build/optimized.wasm.map
Created: ./build/optimized.wat
Created: ./build/untouched.wasm
Created: ./build/untouched.wasm.map
Created: ./build/untouched.wat
 * /

// untouched.wasm is good for debugging
// optimized.wasm can be used in production
// wat is the text representation of the genrated .wasm 

mhadaily

mhadaily

// all other imports 
import { instantiateStreaming, ASUtil } from "assemblyscript/lib/loader";

// no optimization, no memoization
function fibonacci(num) {
  if (num <= 1) return 1;

  return fibonacci(num - 1) + fibonacci(num - 2);
}


class FibWrapper extends Component {
    imports = {};
	wasmService;

  async componentDidMount() {
    this.wasmService = await instantiateStreaming(fetch('/optimized.wasm'), this.imports);
  }

  async factorialwasm() {
    const result = await this.wasmService.fibonacci(9999);
    this.setState({ number: result });
    return;
  }

  factorialJS() {
    const result = fibonacci(9999);
    this.setState({ number: result });
    return;
  }
   render() {
      return <Fib />;
   }
}

mhadaily

mhadaily

mhadaily

mhadaily

Always measure and do the right things

 

 9999-th Fibonacci number

WebAssembly: 0.051025390625ms

Javascript: 0.384765625msms

 

WebAssembly was about 7 times faster for the first run

mhadaily

mhadaily

mhadaily

mhadaily

Worklets

mhadaily

mhadaily

A lightweight version of WebWorkers

Access to low-level parts of rendering pipleline

Run Javascript and WebAssembly

graphic rendering or audio processing

High performance

 Worklets Types

mhadaily

mhadaily

Paint

Animation

Layout

Audio

CSS Paint API

 

(also known as “CSS Custom Paint” or “Houdini’s paint worklet”)

mhadaily

mhadaily

// checkerboard.js
class CheckerboardPainter {
    paint(ctx, geom, properties) {
      // Use `ctx` as if it was a normal canvas
      const colors = ['black', 'white'];
      const size = 32;
      for(let y = 0; y < geom.height/size; y++) {
        for(let x = 0; x < geom.width/size; x++) {
          const color = colors[(x + y) % colors.length];
          ctx.beginPath();
          ctx.fillStyle = color;
          ctx.rect(x * size, y * size, size, size);
          ctx.fill();
        }
      }
    }
  }

// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);

mhadaily

mhadaily

<!-- index.html -->
<!doctype html>
<style>
  textarea {
    background-image: paint(checkerboard);
  }
</style>
<textarea></textarea>
<script>
  if (typeof CSS === 'undefined' || !('paintWorklet' in CSS)) {
    return;
  } else {
	  CSS.paintWorklet.addModule('checkerboard.js');
  }
</script>

mhadaily

mhadaily

mhadaily

mhadaily

mhadaily

mhadaily

Service Worker

All Frameworks

Brain of 

Service Worker in-depth:

https://vimeo.com/273475511

mhadaily

mhadaily

mhadaily

mhadaily

A type of Web Worker

Essentially a Javascript File

Runs different Thread & Background

Intercepts network requests

Caching, delivering push msgs

// Register a Service Worker

// index.html

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
  .then( (reg) => console.log(reg))
  .catch((error) => 
  console.log('Registration failed with ' + error)
  );
}

mhadaily

mhadaily

Why is it interesting?

mhadaily

mhadaily

Why is it interesting?

because you can do many things on the fly!

mhadaily

mhadaily

// all imports and workaround by Kenneth Christiansen
// webp_wasm.wasm

self.addEventListener('fetch', async event => {
  if (event.request.method != 'GET') return;
  if (!event.request.url.endsWith(".webp")) return;

  event.respondWith(async function() {
    try {
      const response = await fetch(event.request);
      const buffer = await response.arrayBuffer();

      const WebPDecoder = await fetchWebPDecoderWithWorkarounds();
      const decoder = new WebPDecoder(buffer);
      const blob = await decoder.decodeToBMP();

      return new Response(blob, { headers: { "content-type": "image/bmp", "status": 200 } });
    } catch(err) {
      console.error(err);
    }

    return fetch(event.request);
  }());
});

mhadaily

mhadaily

Summary

What we have learned

 

mhadaily

mhadaily

Web Workers (comlink)
Web Assembly (AssemblyScript)
Service Worker
Worklets (CSS paint API)
Boost User experince

Majid Hajian

mhadaily

Slides and link to source code

bit.ly/parallel-computing-in-react

majid[at]softiware[dot]com

Parallel Computing in React

By Majid Hajian

Parallel Computing in React

User is working with your application, suddenly, UI freezes and probably, one of the CPU cores is burning! They cannot do anything. The only perception you can feel is as hot as a hell metal case of the laptop. Although this sounds like a horror movie, this is your application that cannot leverage modern APIs to lift heavy computation to a different thread where consequently user suffers the pain. Modern features like Web Workers, WebAssembly, Worklets, and Service Worker allow us to leverage multithreading computing to run tasks parallelly which at the end, makes the user feel like in a rainbow paradise instead of a nightmare, even though JavaScript is a single-threaded programming language! In this session, I am going to show my experience running jobs in parallel on a React application that will provide a pleasant user experience and exciting development.

  • 2,509