Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Multithreaded JavaScript—Web Workers and Atomics

Multithreaded JavaScript—Web Workers and Atomics

For better or worse, JavaScript is single-threaded by design. For over 50 years Moore’s Law has described persistent exponential growth in computational power, but physical limitations have slowed that trend. Advancement now comes through the use of multiple CPUs and cores. This is fine for languages featuring threads and task-based architectures. But what about JavaScript?

Thankfully, new paradigms handle parallelism over the web. Web Worker constructs allow long-running code to execute in the background without blocking the UI. Shared Memory and Atomic operations allow true sharing of data among worker processes. Learn to leverage these exciting features to responsibly improve the performance and responsiveness of your web applications!

Jeff Strauss

June 05, 2018
Tweet

More Decks by Jeff Strauss

Other Decks in Technology

Transcript

  1. #multithreaded J AVA S C R I P T P

    A R A L L E L I S M @ j e f f r e y s t r a u ss
  2. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP CALL STACK EXTERNAL API CALLBACK QUEUE
  3. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() log('Start') CALL STACK EXTERNAL API CALLBACK QUEUE
  4. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API CALLBACK QUEUE setTimeout(...)
  5. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API 100ms timer CALLBACK QUEUE
  6. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API 100ms timer log(2) CALLBACK QUEUE
  7. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API log('Callback') CALLBACK QUEUE
  8. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API log ('Callback') CALLBACK QUEUE
  9. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API CALLBACK QUEUE log ('Callback') log('End')
  10. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP log('Callback') CALL STACK EXTERNAL API CALLBACK QUEUE
  11. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP CALL STACK EXTERNAL API CONSOLE OUTPUT Start 2 End Callback
  12. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 0);

    let sum = 1 + 1; console.log(sum); console.log('End'); } EVENT LOOP CALL STACK EXTERNAL API CONSOLE OUTPUT Start 2 End Callback
  13. function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100);

    let result = blockingCode(); console.log(result); console.log('End'); } EVENT LOOP demo() CALL STACK EXTERNAL API 100ms timer blockingCode() CALLBACK QUEUE
  14. EVENT LOOP demo() CALL STACK EXTERNAL API blockingCode() CALLBACK QUEUE

    this blocks callback execution log ('Callback') function demo() { console.log('Start') setTimeout(() => { console.log('Callback'); }, 100); let result = blockingCode(); console.log(result); console.log('End'); }
  15. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  16. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  17. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  18. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  19. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  20. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  21. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  22. Main says: Talk to me. // printed by worker Worker

    says: Here I am. // printed by main thread CONSOLE OUTPUT
  23. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') close(); }; WORKER.JS
  24. let w = new Worker(worker.js); w.onmessage = (e) => {

    console.log( `Worker says: ${e.data}` ); }; w.postMessage('Talk to me.'); w.terminate(); MAIN.JS onmessage = (e) => { console.log( `Main says: ${e.data}` ); postMessage('Here I am.') }; WORKER.JS
  25. let w = new Worker(worker.js); let foo = { x:

    1, y: 2 }; w.onmessage = (e) => { console.log( `My x is: ${foo.x} Worker x is: ${e.data.x}` ); }; w.postMessage(foo); MAIN.JS let foo; onmessage = (e) => { foo = e.data; foo.x = 42; postMessage(foo) }; WORKER.JS
  26. let w = new Worker(worker.js); let foo = { x:

    1, y: 2 }; w.onmessage = (e) => { console.log( `My x is: ${foo.x} Worker x is: ${e.data.x}` ); }; w.postMessage(foo); MAIN.JS let foo; onmessage = (e) => { foo = e.data; foo.x = 42; postMessage(foo) }; WORKER.JS
  27. let w = new Worker(worker.js); let foo = { x:

    1, y: 2 }; w.onmessage = (e) => { console.log( `My x is: ${foo.x} Worker x is: ${e.data.x}` ); }; w.postMessage(foo); MAIN.JS let foo; onmessage = (e) => { foo = e.data; foo.x = 42; postMessage(foo) }; WORKER.JS
  28. let w = new Worker(worker.js); let foo = { x:

    1, y: 2 }; w.onmessage = (e) => { console.log( `My x is: ${foo.x} Worker x is: ${e.data.x}` ); }; w.postMessage(foo); MAIN.JS let foo; onmessage = (e) => { foo = e.data; foo.x = 42; postMessage(foo) }; WORKER.JS
  29. My x is: 1 // original object literal Worker x

    is: 42 // modified by worker code CONSOLE OUTPUT
  30. let arrBuff = new ArrayBuffer(1024) let foo = { key:

    'foo_buffer', buffer: arrBuff }; let w = new Worker(worker.js); w.postMessage(foo, [arrBuff]); MAIN.JS
  31. let arrBuff = new ArrayBuffer(1024) let foo = { key:

    'foo_buffer', buffer: arrBuff }; let w = new Worker(worker.js); w.postMessage(foo, [arrBuff]); MAIN.JS
  32. // could be any executing context let channel = new

    BroadcastChannel("foo"); // listens to message on channel 'foo' from anyone foo.onmessage = (e) => console.log(e.data); // posts message to anyone who is listening to 'foo' foo.postMesage('Is there anybody out there?'); MAIN.JS / WORKER.JS
  33. // could be any executing context let channel = new

    BroadcastChannel("foo"); // listens to message on channel 'foo' from anyone foo.onmessage = (e) => console.log(e.data); // posts message to anyone who is listening to 'foo' foo.postMesage('Is there anybody out there?'); MAIN.JS / WORKER.JS
  34. // set aside 1KB of shared memory let sab =

    new SharedArrayBuffer(1024); // create a view on the shared memory with 256 Int32 let intArray = new Int32Array(sab); let w = new Worker(worker.js); w.postMessage(intArray); MAIN.JS
  35. // set aside 1KB of shared memory let sab =

    new SharedArrayBuffer(1024); // create a view on the shared memory with 256 Int32 let intArray = new Int32Array(sab); let w = new Worker(worker.js); w.postMessage(intArray); MAIN.JS
  36. // set aside 1KB of shared memory let sab =

    new SharedArrayBuffer(1024); // create a view on the shared memory with 256 Int32 let intArray = new Int32Array(sab); let w = new Worker(worker.js); w.postMessage(intArray); MAIN.JS
  37. let workerIntArray; onmessage = (e) => { // same instance

    accessing the same memory workerIntArray = e.data; Atomics.store(workerIntArray, 42, 500); }; WORKER.JS
  38. W H E R E T O G O F

    R O M H E R E experiment