├── LICENSE ├── README.md ├── dividing-worker ├── app.js ├── index.html └── prime-worker.js ├── main-thread ├── app.js └── index.html ├── shared-buffer-worker ├── app.js ├── index.html └── prime-worker.js ├── simple-worker ├── app.js ├── index.html └── prime-worker.js └── transferring-worker ├── app.js ├── index.html └── prime-worker.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Martin Splitt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-parallelism-demos 2 | Demonstrates how to use Web Workers, transfers and SharedArrayBuffer. 3 | 4 | ## Blog post 5 | 6 | This repository accompanies [this blog post on Javascript parallelism](http://50linesofco.de/post/2017-02-01-javascript-in-parallel-web-workers-transferables-and-sharedarraybuffer). 7 | 8 | ## Content 9 | 10 | The blog post illustrates parallelism in JavaScript using a sample application to find prime numbers. 11 | This repository contains different versions of this application. 12 | 13 | * `/main-thread` contains the simplest version that is blocking the main thread 14 | * `/simple-worker` contains the application using a single worker thread instead of blocking the main thread 15 | * `/dividing-worker` uses multiple workers, cloning the data 16 | * `/transferring-worker` is using multiple workers but transfers data rather than cloning it 17 | * `/shared-buffer-worker` contains a version that uses a `SharedArrayBuffer` to avoid cloning or the downside of transferring data 18 | -------------------------------------------------------------------------------- /dividing-worker/app.js: -------------------------------------------------------------------------------- 1 | document.querySelector('button').addEventListener('click', runTest) 2 | 3 | function runTest() { 4 | const TOTAL_NUMBERS = 1024 * 1024 * 10 5 | const NUM_WORKERS = 4 6 | var numbersToCheck = TOTAL_NUMBERS, primesFound = 0 7 | var buffer = new ArrayBuffer(numbersToCheck) // reserves 10 MB 8 | var view = new Uint8Array(buffer) // view the buffer as bytes 9 | 10 | performance.mark('testStart') 11 | var offset = 0 12 | performance.mark('launchWorkerStart') 13 | while(numbersToCheck) { 14 | var blockLen = Math.min(numbersToCheck, TOTAL_NUMBERS / NUM_WORKERS) 15 | var worker = new Worker('prime-worker.js') 16 | worker.onmessage = function(msg) { 17 | performance.mark('workerResponseStart') 18 | view.set(new Uint8Array(msg.data.buffer), msg.data.offset) 19 | primesFound += msg.data.numPrimes 20 | 21 | if(msg.data.offset + msg.data.length === buffer.byteLength) { 22 | performance.mark('testEnd') 23 | performance.measure('runTest', 'testStart', 'testEnd') 24 | var timeTaken = performance.getEntriesByName('runTest')[0].duration 25 | alert(`Done. Found ${primesFound} primes in ${timeTaken} ms`) 26 | console.log(primesFound, view) 27 | } 28 | performance.mark('workerResponseEnd') 29 | performance.measure('workerResponse', 'workerResponseStart', 'workerResponseEnd') 30 | } 31 | 32 | worker.postMessage({ 33 | offset: offset, 34 | length: blockLen 35 | }) 36 | 37 | numbersToCheck -= blockLen 38 | offset += blockLen 39 | performance.mark('launchWorkerEnd') 40 | performance.measure('launchWorker', 'launchWorkerStart', 'launchWorkerEnd') 41 | } 42 | } -------------------------------------------------------------------------------- /dividing-worker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /dividing-worker/prime-worker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = function(msg) { 2 | var view = new Uint8Array(msg.data.length), 3 | numPrimes = 0 4 | for(var i=0; i 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /shared-buffer-worker/app.js: -------------------------------------------------------------------------------- 1 | document.querySelector('button').addEventListener('click', runTest) 2 | 3 | function runTest() { 4 | const TOTAL_NUMBERS = 1024 * 1024 * 10 5 | const NUM_WORKERS = 4 6 | var numbersToCheck = TOTAL_NUMBERS, primesFound = 0 7 | var buffer = new SharedArrayBuffer(numbersToCheck) // reserves 10 MB 8 | var view = new Uint8Array(buffer) // view the buffer as bytes 9 | 10 | performance.mark('testStart') 11 | var offset = 0 12 | while(numbersToCheck) { 13 | var blockLen = Math.min(numbersToCheck, TOTAL_NUMBERS / NUM_WORKERS) 14 | var worker = new Worker('prime-worker.js') 15 | worker.onmessage = function(msg) { 16 | primesFound += msg.data.numPrimes 17 | 18 | if(msg.data.offset + msg.data.length === buffer.byteLength) { 19 | performance.mark('testEnd') 20 | performance.measure('runTest', 'testStart', 'testEnd') 21 | var timeTaken = performance.getEntriesByName('runTest')[0].duration 22 | alert(`Done. Found ${primesFound} primes in ${timeTaken} ms`) 23 | console.log(primesFound, view) 24 | } 25 | } 26 | 27 | worker.postMessage({ 28 | buffer: buffer, 29 | offset: offset, 30 | length: blockLen 31 | }) 32 | 33 | numbersToCheck -= blockLen 34 | offset += blockLen 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shared-buffer-worker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /shared-buffer-worker/prime-worker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = function(msg) { 2 | var view = new Uint8Array(msg.data.buffer, msg.data.offset, msg.data.length), 3 | numPrimes = 0 4 | 5 | console.log('start', msg.data.offset+2, 'before', msg.data.offset+2+msg.data.length) 6 | for(var i=0; i 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /simple-worker/prime-worker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = function(msg) { 2 | var view = new Uint8Array(msg.data), 3 | numPrimes = 0 4 | for(var i=0; i 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /transferring-worker/prime-worker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = function(msg) { 2 | var view = new Uint8Array(msg.data), 3 | numPrimes = 0 4 | for(var i=0; i