├── .gitignore ├── NOTES.md ├── README.md ├── array ├── calcPrimes.js └── index.js ├── arrayBuffer ├── calcPrimes.js └── index.js ├── benchmark.csv ├── benchmarking.js ├── index.js ├── normalSieve.js ├── package-lock.json ├── package.json ├── sharedArrayBuffer ├── calcPrimes.js └── index.js └── static ├── graph.png ├── workerBench.gif └── workerIndex.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | ### Async Await 2 | - A function has to be async if it uses await keyword within it 3 | - Await keyword can be used to wait for promise to get resolved or async functions to get resolved 4 | - An async function can be awaited as well 5 | 6 | ### Promises 7 | - Returns a promise object which can either return resolve, or reject and accordingly call .then() or .catch() 8 | - After a `Promise` is resolved, it cannot go back to pending state. More so, causing `Promise.all()` to execute for each single instance as well, now. 9 | 10 | ### ArrayBuffer.transfer() 11 | - [ArrayBuffer.transfer()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/transfer) method is still in experimental phase, so had to slice it, like polyfills would 12 | 13 | ### Array.forEach() 14 | - Can't use a break statement in it 15 | 16 | ### Module Exports 17 | - While requiring an exported module, the parser goes through the whole of required file, and executes everything, if a function is called, that will run 18 | - Whereas only the exported ones are put into context 19 | 20 | 21 | ### Errors 22 | - Need to fill Array object before passing, else recieve something like - `<2 empty items>` in console. 23 | - Fixed relative path issues while exporting, use `__dirname` or other special variables like `__filename` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Idea 2 | ## Primes 3 | - Calculating primes is one of the most computationally intensive task, and an important component in computer security. 4 | - This repo aims at calculating prime numbers, in a certain range, using multithreading concepts in NodeJS. 5 | - Basic sieve method is used to collect prime numbers. The range is divided between worker-threads here. 6 | - And the number of worker threads spawned is equal to the number of cores, to maximise output. If more spawned need to switch between threads, which takes time, if more spawned, cores underutilized. 7 | 8 | ## Worker Threads 9 | - Javascript can handle I/O events easily, owing to EventLoop. Hence, Node can handle multiple HTTP requests pretty seamlessly. 10 | - But, if Node needs to do heavy computation, it cannot rely on EventLoop, since it runs on single-thread, and all the cores aren't optimally utilized. Here is when, `worker-thread` comes in. 11 | - From the Worker-Thread Docs- 12 | `Workers (threads) are useful for performing CPU-intensive JavaScript operations. They will not help much with I/O-intensive work. Node.js’s built-in asynchronous I/O operations are more efficient than Workers can be.` 13 | - For tasks like these, even `child_processes` or `cluster` can be used. Although, they don't provide support for transferring or sharing memory as of now. 14 | 15 | ### Links 16 | - [EventLoop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop) 17 | - [Worker-Thread Docs](https://nodejs.org/api/worker_threads.html#worker_threads_worker_threads) 18 | 19 | ## Data Structure 20 | - For initialising a worker, passing data in workerData actually clones the data 21 | - Whereas, for passing messages, using `worker.postMessage()`, data is passed either by cloning, transfering or sharing 22 | - Cloning : `Arrays` are serialised then cloned, and desearialised. Since, the raw data segregation isn't know. 23 | - Transfering : `ArrayBuffer` operate on this, since, they use `TypedArrays` such as `Uint8Array` as layer, owing to which, their raw segment sizes are known, and can be transfered easily. 24 | - Sharing : `SharedArrayBuffer` is similar to ArrayBuffer, where you need to wrap it using `TypedArrays`, but they can be shared. To maintain concurrency, `Atomics` library is used alongwith it, to perform concurrent opertations 25 | 26 | ### Links 27 | - [Array - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) 28 | - [ArrayBuffer - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) 29 | - [SharedArrayBuffer - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) 30 | - [TypedArray - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) 31 | - [A crash course in memory management - Mozilla Hacks](https://hacks.mozilla.org/2017/06/a-crash-course-in-memory-management/) 32 | 33 | ## Other Tools used 34 | - [Inquirer.JS](https://github.com/SBoudrias/Inquirer.js/) - To create command line interfaces, in a whim. 35 | - [Ora](https://github.com/sindresorhus/ora) - Elegant terminal spinners for your command line interfaces. 36 | - [Benchmark.JS](https://github.com/bestiejs/benchmark.js/) - A robust benchmarking library, with high-resolution timers. 37 | 38 | # Interface 39 | ## Interactive CLI 40 | 41 | 42 | ## Benchmarking and write to CSV 43 | 44 | 45 | # ToDo 46 | - [ ] Add serialised Array and benchmark 47 | - [x] Plot the benchmark data 48 | - [ ] Benchmark message passing 49 | - [ ] Implement communication between workers 50 | 51 | # Benchmarks 52 | - To calculate prime numbers upto a Range. 53 | ## CPU 54 | `CPU family: 6 ` 55 | `Model name: Intel(R) Core(TM) i5-8600K CPU @ 3.60GHz ` 56 | 57 | ## OS 58 | `Ubuntu 18.04` 59 | 60 | ## Graph 61 | 62 | 63 | ## Data 64 | 65 | | Range | Thread | DataStructure | MeanExecTime(in secs) | NumberOfCycles | 66 | |----------|--------|-------------------|--------------------------|----------------| 67 | | 100 | worker | sharedArrayBuffer | 0.05451655452083334 | 48 | 68 | | 100 | worker | arrayBuffer | 0.05048775225490198 | 51 | 69 | | 100 | worker | array | 0.05209960375510204 | 49 | 70 | | 100 | main | normalSieve | 0.0000026189117609852627 | 88 | 71 | | 1000 | worker | sharedArrayBuffer | 0.05594300798936169 | 47 | 72 | | 1000 | worker | arrayBuffer | 0.06054065555844155 | 77 | 73 | | 1000 | worker | array | 0.06030849465384614 | 78 | 74 | | 1000 | main | normalSieve | 0.000025705733772647114 | 88 | 75 | | 10000 | worker | sharedArrayBuffer | 0.07244938477611937 | 67 | 76 | | 10000 | worker | arrayBuffer | 0.05967241462068965 | 58 | 77 | | 10000 | worker | array | 0.06134170045454545 | 77 | 78 | | 10000 | main | normalSieve | 0.00028022878913731803 | 83 | 79 | | 100000 | worker | sharedArrayBuffer | 0.06629550497260277 | 73 | 80 | | 100000 | worker | arrayBuffer | 0.06146969272727272 | 77 | 81 | | 100000 | worker | array | 0.07276433691044777 | 67 | 82 | | 100000 | main | normalSieve | 0.003791498413793104 | 87 | 83 | | 1000000 | worker | sharedArrayBuffer | 0.08153615076666666 | 60 | 84 | | 1000000 | worker | arrayBuffer | 0.08271871261666669 | 60 | 85 | | 1000000 | worker | array | 0.15392867627777776 | 36 | 86 | | 1000000 | main | normalSieve | 0.04862653041509433 | 53 | 87 | | 10000000 | worker | sharedArrayBuffer | 0.1780489309032258 | 31 | 88 | | 10000000 | worker | arrayBuffer | 0.23233239540000003 | 25 | 89 | | 10000000 | worker | array | 1.465544911125 | 8 | 90 | | 10000000 | main | normalSieve | 1.631624435625 | 8 | 91 | | 20000000 | worker | sharedArrayBuffer | 0.36814615133333334 | 18 | 92 | | 20000000 | worker | arrayBuffer | 0.8236841264000001 | 10 | 93 | | 20000000 | worker | array | 3.2359141328333334 | 6 | 94 | | 20000000 | main | normalSieve | 3.213333790833333 | 6 | 95 | 96 | 97 | - Over large inputs, worker thread works better than main threads. 98 | - Also, sharedArrayBuffer gives best performance over various data structures. 99 | - When using sharedArrayBuffer beware, to use Atomics library to maintain data consistency. 100 | # Addendum 101 | - Notes and errors encounterd, have been added to [NOTES.md](./NOTES.md) 102 | 103 | # Contributing 104 | - Always open for `enchancements` and `bux-fixes`! 105 | 106 | ### Contributors 107 | - [@nilshah98](https://github.com/nilshah98) 108 | 109 | -------------------------------------------------------------------------------- /array/calcPrimes.js: -------------------------------------------------------------------------------- 1 | const { Worker, parentPort, workerData } = require("worker_threads"); 2 | 3 | // Get init data from mainThread 4 | const size = workerData.size; 5 | const start = workerData.start; 6 | const limit = workerData.limit; 7 | var curr = workerData.curr; 8 | const sieve = workerData.sieve; 9 | 10 | // While the current factor is less than eq limit 11 | // While the current factor is less than eq last element of thread 12 | while (curr <= limit && curr <= start + size - 1) { 13 | // if sieve isn't marked by anyone else 14 | // alter to >=0 to check performance for repeating elements 15 | 16 | // If start is not multiple of curr, index = the extra elems needed 17 | if (start % curr) { 18 | var index = curr - (start % curr); 19 | } else { 20 | // if start is multiple of curr, index = 0 21 | var index = 0; 22 | } 23 | 24 | // loop through each index, incrementing by curr, till size of segment exhausted 25 | for (index; index < size; index += curr) { 26 | // Not changing if start+index is same as curr 27 | if (start + index != curr) { 28 | // Else altering flag to 1, as factor found 29 | // -2, since 0 indexed and first element is 2 and not 1 30 | sieve[index] = 1; 31 | } 32 | } 33 | 34 | // incrementing curr here => 35 | curr += 1; 36 | } 37 | 38 | parentPort.postMessage(sieve); 39 | parentPort.close(); 40 | -------------------------------------------------------------------------------- /array/index.js: -------------------------------------------------------------------------------- 1 | const os = require("os"); 2 | const path = require("path"); 3 | const { Worker, parentPort, workerData } = require("worker_threads"); 4 | 5 | const cpuCount = os.cpus().length; 6 | const workerPath = path.resolve(__dirname + "/calcPrimes.js"); 7 | 8 | const calculatePrimes = number => { 9 | return new Promise((resolve, reject) => { 10 | // constant cannot be changed through re-assignment 11 | // constant cannot be changed through re-declaration 12 | const numbers = []; 13 | 14 | // Creating a shared array buffer of size => number-1, since 1 is not counted for prime 15 | // ArrayBuffer.transfer() in experimental stages so can't use 16 | const area = new Array(number - 1).fill(0); 17 | // const sieve = new Int8Array(area); 18 | 19 | // When passing a whole array to worker thread, it makes a copy of the array and no changes made there 20 | // Gives a stack overflow error even for calculating upto 1000000 as a copy is made and passed to all threads 21 | // const sieve = new Array(number-1).fill(0); 22 | 23 | // Get start value and size of each segment, that is passed to the threads 24 | 25 | // ceil so as to distribute the leftout equally among threads 26 | const segmentSize = Math.ceil((number - 1) / cpuCount); 27 | var segments = []; 28 | 29 | for (let segmentIndex = 0; segmentIndex < cpuCount; segmentIndex++) { 30 | const start = segmentIndex * segmentSize; 31 | const segment = [start, segmentSize]; 32 | segments.push(segment); 33 | } 34 | 35 | // The limit till which we need to check for primes 36 | const limit = Math.ceil(Math.sqrt(number)); 37 | 38 | // initialise worker with static global data 39 | const workers = segments.map(segment => { 40 | // Create a promise for each worker 41 | return new Promise((resolve, reject) => { 42 | // creating a worker with the some inital workerData 43 | const worker = new Worker(workerPath, { 44 | workerData: { 45 | start: segment[0], 46 | size: segment[1], 47 | curr: 2, 48 | limit: limit, 49 | sieve: area.slice(0,segment[1]) 50 | } 51 | }); 52 | 53 | // When worker emits, message | error | exit events 54 | worker.on("message", resolve); 55 | worker.on("error", reject); 56 | worker.on("exit", code => { 57 | if (code !== 0) 58 | reject( 59 | new Error(`Worker stopped with exit code ${code}`) 60 | ); 61 | }); 62 | }); 63 | }); 64 | 65 | // When all the workers have completed processing, and returned data in order, and in sync 66 | Promise.all(workers).then(res => { 67 | // Numbers passed from 0, so need to reduce 2 in the final answer 68 | // Iterating through shared buffer once, all threads have parsed, and adding elems having 0 to primes 69 | let counter = 0; 70 | const primes = []; 71 | res.forEach((sieve) => { 72 | sieve.forEach(elem => { 73 | if (elem == 0 && counter >= 2 && counter <= number) { 74 | primes.push(counter); 75 | } 76 | counter += 1; 77 | }); 78 | }) 79 | 80 | 81 | 82 | // Logging number of primes, and the time to process 83 | 84 | resolve(primes.length); 85 | }); 86 | }); 87 | }; 88 | 89 | module.exports = calculatePrimes; -------------------------------------------------------------------------------- /arrayBuffer/calcPrimes.js: -------------------------------------------------------------------------------- 1 | const { Worker, parentPort, workerData } = require("worker_threads"); 2 | 3 | // Get init data from mainThread 4 | const size = workerData.size; 5 | const start = workerData.start; 6 | const limit = workerData.limit; 7 | var curr = workerData.curr; 8 | const sieve = new Int8Array(workerData.sieve); 9 | 10 | // While the current factor is less than eq limit 11 | // While the current factor is less than eq last element of thread 12 | while (curr <= limit && curr <= start + size - 1) { 13 | // if sieve isn't marked by anyone else 14 | // alter to >=0 to check performance for repeating elements 15 | 16 | // If start is not multiple of curr, index = the extra elems needed 17 | if (start % curr) { 18 | var index = curr - (start % curr); 19 | } else { 20 | // if start is multiple of curr, index = 0 21 | var index = 0; 22 | } 23 | 24 | // loop through each index, incrementing by curr, till size of segment exhausted 25 | for (index; index < size; index += curr) { 26 | // Not changing if start+index is same as curr 27 | if (start + index != curr) { 28 | // Else altering flag to 1, as factor found 29 | // -2, since 0 indexed and first element is 2 and not 1 30 | sieve[index] = 1; 31 | } 32 | } 33 | 34 | // incrementing curr here => 35 | curr += 1; 36 | } 37 | 38 | parentPort.postMessage(sieve); 39 | parentPort.close(); 40 | -------------------------------------------------------------------------------- /arrayBuffer/index.js: -------------------------------------------------------------------------------- 1 | const os = require("os"); 2 | const path = require("path"); 3 | const { Worker, parentPort, workerData } = require("worker_threads"); 4 | 5 | const cpuCount = os.cpus().length; 6 | const workerPath = path.resolve(__dirname + "/calcPrimes.js"); 7 | 8 | const calculatePrimes = number => { 9 | return new Promise((resolve, reject) => { 10 | // constant cannot be changed through re-assignment 11 | // constant cannot be changed through re-declaration 12 | const numbers = []; 13 | 14 | // Creating a shared array buffer of size => number-1, since 1 is not counted for prime 15 | // ArrayBuffer.transfer() in experimental stages so can't use 16 | const area = new ArrayBuffer(number - 1); 17 | const sieve = new Int8Array(area); 18 | 19 | // When passing a whole array to worker thread, it makes a copy of the array and no changes made there 20 | // Gives a stack overflow error even for calculating upto 1000000 as a copy is made and passed to all threads 21 | // const sieve = new Array(number-1).fill(0); 22 | 23 | // Get start value and size of each segment, that is passed to the threads 24 | 25 | // ceil so as to distribute the leftout equally among threads 26 | const segmentSize = Math.ceil((number - 1) / cpuCount); 27 | var segments = []; 28 | 29 | for (let segmentIndex = 0; segmentIndex < cpuCount; segmentIndex++) { 30 | const start = segmentIndex * segmentSize; 31 | const segment = [start, segmentSize]; 32 | segments.push(segment); 33 | } 34 | 35 | // The limit till which we need to check for primes 36 | const limit = Math.ceil(Math.sqrt(number)); 37 | 38 | // initialise worker with static global data 39 | const workers = segments.map(segment => { 40 | // Create a promise for each worker 41 | return new Promise((resolve, reject) => { 42 | // creating a worker with the some inital workerData 43 | const worker = new Worker(workerPath, { 44 | workerData: { 45 | start: segment[0], 46 | size: segment[1], 47 | curr: 2, 48 | limit: limit, 49 | sieve: area.slice(0,segment[1]) 50 | } 51 | }); 52 | 53 | // When worker emits, message | error | exit events 54 | worker.on("message", resolve); 55 | worker.on("error", reject); 56 | worker.on("exit", code => { 57 | if (code !== 0) 58 | reject( 59 | new Error(`Worker stopped with exit code ${code}`) 60 | ); 61 | }); 62 | }); 63 | }); 64 | 65 | // When all the workers have completed processing, and returned data in order, and in sync 66 | Promise.all(workers).then(res => { 67 | // Numbers passed from 0, so need to reduce 2 in the final answer 68 | // Iterating through shared buffer once, all threads have parsed, and adding elems having 0 to primes 69 | let counter = 0; 70 | const primes = []; 71 | res.forEach((sieve) => { 72 | sieve.forEach(elem => { 73 | if (elem == 0 && counter >= 2 && counter <= number) { 74 | primes.push(counter); 75 | } 76 | counter += 1; 77 | }); 78 | }) 79 | 80 | 81 | 82 | // Logging number of primes, and the time to process 83 | 84 | resolve(primes.length); 85 | }); 86 | }); 87 | }; 88 | 89 | module.exports = calculatePrimes; -------------------------------------------------------------------------------- /benchmark.csv: -------------------------------------------------------------------------------- 1 | 100,worker,sharedArrayBuffer,0.05451655452083334,48 2 | 100,worker,arrayBuffer,0.05048775225490198,51 3 | 100,worker,array,0.05209960375510204,49 4 | 100,main,normalSieve,0.0000026189117609852627,88 5 | 1000,worker,sharedArrayBuffer,0.05594300798936169,47 6 | 1000,worker,arrayBuffer,0.06054065555844155,77 7 | 1000,worker,array,0.06030849465384614,78 8 | 1000,main,normalSieve,0.000025705733772647114,88 9 | 10000,worker,sharedArrayBuffer,0.07244938477611937,67 10 | 10000,worker,arrayBuffer,0.05967241462068965,58 11 | 10000,worker,array,0.06134170045454545,77 12 | 10000,main,normalSieve,0.00028022878913731803,83 13 | 100000,worker,sharedArrayBuffer,0.06629550497260277,73 14 | 100000,worker,arrayBuffer,0.06146969272727272,77 15 | 100000,worker,array,0.07276433691044777,67 16 | 100000,main,normalSieve,0.003791498413793104,87 17 | 1000000,worker,sharedArrayBuffer,0.08153615076666666,60 18 | 1000000,worker,arrayBuffer,0.08271871261666669,60 19 | 1000000,worker,array,0.15392867627777776,36 20 | 1000000,main,normalSieve,0.04862653041509433,53 21 | 10000000,worker,sharedArrayBuffer,0.1780489309032258,31 22 | 10000000,worker,arrayBuffer,0.23233239540000003,25 23 | 10000000,worker,array,1.465544911125,8 24 | 10000000,main,normalSieve,1.631624435625,8 25 | 20000000,worker,sharedArrayBuffer,0.36814615133333334,18 26 | 20000000,worker,arrayBuffer,0.8236841264000001,10 27 | 20000000,worker,array,3.2359141328333334,6 28 | 20000000,main,normalSieve,3.213333790833333,6 29 | -------------------------------------------------------------------------------- /benchmarking.js: -------------------------------------------------------------------------------- 1 | const Benchmark = require("benchmark"); 2 | const ora = require("ora"); 3 | const inquirer = require("inquirer"); 4 | const createCsvWriter = require("csv-writer").createObjectCsvWriter; 5 | 6 | const spinner = ora("Starting Benchmarking"); 7 | const suite = new Benchmark.Suite({ 8 | onStart: () => { 9 | spinner.start("Starting Benchmarking"); 10 | }, 11 | onComplete: () => { 12 | spinner.stop(); 13 | } 14 | }); 15 | const csvWriter = createCsvWriter({ 16 | append: true, 17 | path: "benchmark.csv", 18 | header: [ 19 | { id: "range", title: "Range" }, 20 | { id: "thread", title: "Thread" }, 21 | { id: "DS", title: "DS" }, 22 | { id: "meanTime", title: "Mean-Time" }, 23 | { id: "noOfTest", title: "No.-Of-Tests" } 24 | ] 25 | }); 26 | 27 | const sharedBufferPrime = require("./sharedArrayBuffer"); 28 | const arrayBuffer = require("./arrayBuffer"); 29 | const array = require("./array"); 30 | const normalSieve = require("./normalSieve"); 31 | 32 | const data = []; 33 | const run = async () => { 34 | const { primeRange } = await inquirer.prompt([ 35 | { 36 | type: "input", 37 | name: "primeRange", 38 | message: "Benchmark till ?", 39 | default: 100 40 | } 41 | ]); 42 | 43 | suite 44 | .add(String(primeRange) + " sharedArrayBuffer", { 45 | defer: true, 46 | fn: async deferred => { 47 | await sharedBufferPrime(primeRange); 48 | 49 | deferred.resolve(); 50 | } 51 | }) 52 | .add(String(primeRange) + " arrayBuffer", { 53 | defer: true, 54 | fn: async deferred => { 55 | await arrayBuffer(primeRange); 56 | deferred.resolve(); 57 | } 58 | }) 59 | .add(String(primeRange) + " array", { 60 | defer: true, 61 | fn: async deferred => { 62 | await array(primeRange); 63 | deferred.resolve(); 64 | } 65 | }) 66 | .add(String(primeRange) + " normalSieve", { 67 | defer: true, 68 | fn: async deferred => { 69 | await normalSieve(primeRange); 70 | deferred.resolve(); 71 | } 72 | }) 73 | // add listeners 74 | .on("cycle", function(event) { 75 | spinner.stopAndPersist({ 76 | text: `${event.target.name} Cycle finished`, 77 | symbol: "⌛" 78 | }); 79 | if(event.target.name.split(" ")[1] == "normalSieve"){ 80 | var label = "main"; 81 | }else{ 82 | var label = "worker" 83 | } 84 | data.push({ 85 | range: event.target.name.split(" ")[0], 86 | thread: label, 87 | DS: event.target.name.split(" ")[1], 88 | meanTime: event.target.stats.mean, 89 | noOfTest: event.target.stats.sample.length 90 | }); 91 | console.log(`Mean Exec time : ${event.target.stats.mean} seconds`); 92 | console.log( 93 | "Number of tests : " + event.target.stats.sample.length 94 | ); 95 | spinner.start(); 96 | }) 97 | .on("complete", function() { 98 | // console.log('Fastest is ' + this.filter('fastest').map('name')); 99 | csvWriter 100 | .writeRecords(data) 101 | .then(() => 102 | console.log("The CSV file was written successfully") 103 | ); 104 | }) 105 | // run async 106 | .run({ async: true }); 107 | }; 108 | 109 | run(); 110 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | const ora = require("ora"); 3 | 4 | const array = require("./array"); 5 | const arrayBuffer = require("./arrayBuffer"); 6 | const sharedArrayBuffer = require("./sharedArrayBuffer"); 7 | const normalSieve = require("./normalSieve"); 8 | 9 | const NS_PER_SEC = 1e9; 10 | 11 | const calcTime = async (inp,func,label) => { 12 | const spinner = ora("Calculating number of primes-").start(); 13 | const stTime = process.hrtime(); 14 | var res = await func(inp); 15 | const endTime = process.hrtime(stTime); 16 | spinner.succeed(` (${label}) Number of primes : ${res}`); 17 | const time = endTime[0] * NS_PER_SEC + endTime[1]; 18 | spinner.stopAndPersist( 19 | { 20 | "text":`(${label}) Benchmark took : ${time/NS_PER_SEC} seconds\n`, 21 | "symbol":"⌛" 22 | } 23 | ); 24 | return time; 25 | } 26 | 27 | const run = async () => { 28 | const { primeRange } = await inquirer.prompt([ 29 | { 30 | type: "input", 31 | name: "primeRange", 32 | message: "Find primes till ?", 33 | default: 100 34 | } 35 | ]); 36 | 37 | // Written using then 38 | // calculatePrimes(primeRange).then((res) => { 39 | // spinner.succeed(`Number of primes : ${res[0]}`) 40 | // }); 41 | 42 | const workerArrayTime = await calcTime(primeRange,array,"worker array"); 43 | const workerArrayBufferTime = await calcTime(primeRange,arrayBuffer,"worker arrayBuffer"); 44 | const workerSharedArrayBufferTime = await calcTime(primeRange,sharedArrayBuffer,"worker sharedArrayBuffer"); 45 | const localTime = await calcTime(primeRange,normalSieve,"main"); 46 | console.log(`⏱️ Time difference between main thread and worker (sharedArrayBuffer) : ${(localTime-workerSharedArrayBufferTime)/NS_PER_SEC} seconds`); 47 | 48 | }; 49 | 50 | run(); -------------------------------------------------------------------------------- /normalSieve.js: -------------------------------------------------------------------------------- 1 | const normalSieve = n => { 2 | return new Promise((resolve, reject) => { 3 | // Eratosthenes algorithm to find all primes under n 4 | var array = [], 5 | upperLimit = Math.sqrt(n), 6 | output = []; 7 | 8 | // Make an array from 2 to (n - 1) 9 | for (var i = 0; i < n; i++) { 10 | array.push(true); 11 | } 12 | 13 | // Remove multiples of primes starting from 2, 3, 5,... 14 | for (var i = 2; i <= upperLimit; i++) { 15 | if (array[i]) { 16 | for (var j = i * i; j < n; j += i) { 17 | array[j] = false; 18 | } 19 | } 20 | } 21 | 22 | // All array[i] set to true are primes 23 | for (var i = 2; i < n; i++) { 24 | if (array[i]) { 25 | output.push(i); 26 | } 27 | } 28 | 29 | resolve(output.length); 30 | }); 31 | }; 32 | 33 | module.exports = normalSieve; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker-threads-nodejs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-escapes": { 8 | "version": "3.2.0", 9 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", 10 | "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" 11 | }, 12 | "ansi-regex": { 13 | "version": "3.0.0", 14 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 15 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 16 | }, 17 | "ansi-styles": { 18 | "version": "3.2.1", 19 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 20 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 21 | "requires": { 22 | "color-convert": "^1.9.0" 23 | } 24 | }, 25 | "benchmark": { 26 | "version": "2.1.4", 27 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", 28 | "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", 29 | "requires": { 30 | "lodash": "^4.17.4", 31 | "platform": "^1.3.3" 32 | } 33 | }, 34 | "chalk": { 35 | "version": "2.4.2", 36 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 37 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | }, 44 | "chardet": { 45 | "version": "0.7.0", 46 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", 47 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" 48 | }, 49 | "cli-cursor": { 50 | "version": "2.1.0", 51 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", 52 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", 53 | "requires": { 54 | "restore-cursor": "^2.0.0" 55 | } 56 | }, 57 | "cli-spinners": { 58 | "version": "2.0.0", 59 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.0.0.tgz", 60 | "integrity": "sha512-yiEBmhaKPPeBj7wWm4GEdtPZK940p9pl3EANIrnJ3JnvWyrPjcFcsEq6qRUuQ7fzB0+Y82ld3p6B34xo95foWw==" 61 | }, 62 | "cli-width": { 63 | "version": "2.2.0", 64 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 65 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" 66 | }, 67 | "clone": { 68 | "version": "1.0.4", 69 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", 70 | "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" 71 | }, 72 | "color-convert": { 73 | "version": "1.9.3", 74 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 75 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 76 | "requires": { 77 | "color-name": "1.1.3" 78 | } 79 | }, 80 | "color-name": { 81 | "version": "1.1.3", 82 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 83 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 84 | }, 85 | "csv-writer": { 86 | "version": "1.2.0", 87 | "resolved": "https://registry.npmjs.org/csv-writer/-/csv-writer-1.2.0.tgz", 88 | "integrity": "sha512-ZU7eZ26y7oIOXwFHhTNnT/tF6RXMdQnuTGofzNTe1cvXacZm3Ixd7Oa9mOp2f1t39Ezfzg0h5tqfOjXM180BOg==" 89 | }, 90 | "defaults": { 91 | "version": "1.0.3", 92 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", 93 | "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", 94 | "requires": { 95 | "clone": "^1.0.2" 96 | } 97 | }, 98 | "escape-string-regexp": { 99 | "version": "1.0.5", 100 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 101 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 102 | }, 103 | "external-editor": { 104 | "version": "3.0.3", 105 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", 106 | "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", 107 | "requires": { 108 | "chardet": "^0.7.0", 109 | "iconv-lite": "^0.4.24", 110 | "tmp": "^0.0.33" 111 | } 112 | }, 113 | "figures": { 114 | "version": "2.0.0", 115 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", 116 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", 117 | "requires": { 118 | "escape-string-regexp": "^1.0.5" 119 | } 120 | }, 121 | "has-flag": { 122 | "version": "3.0.0", 123 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 124 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 125 | }, 126 | "iconv-lite": { 127 | "version": "0.4.24", 128 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 129 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 130 | "requires": { 131 | "safer-buffer": ">= 2.1.2 < 3" 132 | } 133 | }, 134 | "inquirer": { 135 | "version": "6.2.2", 136 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", 137 | "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", 138 | "requires": { 139 | "ansi-escapes": "^3.2.0", 140 | "chalk": "^2.4.2", 141 | "cli-cursor": "^2.1.0", 142 | "cli-width": "^2.0.0", 143 | "external-editor": "^3.0.3", 144 | "figures": "^2.0.0", 145 | "lodash": "^4.17.11", 146 | "mute-stream": "0.0.7", 147 | "run-async": "^2.2.0", 148 | "rxjs": "^6.4.0", 149 | "string-width": "^2.1.0", 150 | "strip-ansi": "^5.0.0", 151 | "through": "^2.3.6" 152 | } 153 | }, 154 | "is-fullwidth-code-point": { 155 | "version": "2.0.0", 156 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 157 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 158 | }, 159 | "is-promise": { 160 | "version": "2.1.0", 161 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 162 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" 163 | }, 164 | "lodash": { 165 | "version": "4.17.11", 166 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 167 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 168 | }, 169 | "log-symbols": { 170 | "version": "2.2.0", 171 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 172 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 173 | "requires": { 174 | "chalk": "^2.0.1" 175 | } 176 | }, 177 | "mimic-fn": { 178 | "version": "1.2.0", 179 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 180 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" 181 | }, 182 | "mute-stream": { 183 | "version": "0.0.7", 184 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 185 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" 186 | }, 187 | "onetime": { 188 | "version": "2.0.1", 189 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", 190 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", 191 | "requires": { 192 | "mimic-fn": "^1.0.0" 193 | } 194 | }, 195 | "ora": { 196 | "version": "3.2.0", 197 | "resolved": "https://registry.npmjs.org/ora/-/ora-3.2.0.tgz", 198 | "integrity": "sha512-XHMZA5WieCbtg+tu0uPF8CjvwQdNzKCX6BVh3N6GFsEXH40mTk5dsw/ya1lBTUGJslcEFJFQ8cBhOgkkZXQtMA==", 199 | "requires": { 200 | "chalk": "^2.4.2", 201 | "cli-cursor": "^2.1.0", 202 | "cli-spinners": "^2.0.0", 203 | "log-symbols": "^2.2.0", 204 | "strip-ansi": "^5.0.0", 205 | "wcwidth": "^1.0.1" 206 | } 207 | }, 208 | "os-tmpdir": { 209 | "version": "1.0.2", 210 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 211 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 212 | }, 213 | "platform": { 214 | "version": "1.3.5", 215 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", 216 | "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==" 217 | }, 218 | "restore-cursor": { 219 | "version": "2.0.0", 220 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 221 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", 222 | "requires": { 223 | "onetime": "^2.0.0", 224 | "signal-exit": "^3.0.2" 225 | } 226 | }, 227 | "run-async": { 228 | "version": "2.3.0", 229 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 230 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 231 | "requires": { 232 | "is-promise": "^2.1.0" 233 | } 234 | }, 235 | "rxjs": { 236 | "version": "6.4.0", 237 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", 238 | "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", 239 | "requires": { 240 | "tslib": "^1.9.0" 241 | } 242 | }, 243 | "safer-buffer": { 244 | "version": "2.1.2", 245 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 246 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 247 | }, 248 | "signal-exit": { 249 | "version": "3.0.2", 250 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 251 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 252 | }, 253 | "string-width": { 254 | "version": "2.1.1", 255 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 256 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 257 | "requires": { 258 | "is-fullwidth-code-point": "^2.0.0", 259 | "strip-ansi": "^4.0.0" 260 | }, 261 | "dependencies": { 262 | "strip-ansi": { 263 | "version": "4.0.0", 264 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 265 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 266 | "requires": { 267 | "ansi-regex": "^3.0.0" 268 | } 269 | } 270 | } 271 | }, 272 | "strip-ansi": { 273 | "version": "5.2.0", 274 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 275 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 276 | "requires": { 277 | "ansi-regex": "^4.1.0" 278 | }, 279 | "dependencies": { 280 | "ansi-regex": { 281 | "version": "4.1.0", 282 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 283 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 284 | } 285 | } 286 | }, 287 | "supports-color": { 288 | "version": "5.5.0", 289 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 290 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 291 | "requires": { 292 | "has-flag": "^3.0.0" 293 | } 294 | }, 295 | "through": { 296 | "version": "2.3.8", 297 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 298 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 299 | }, 300 | "tmp": { 301 | "version": "0.0.33", 302 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 303 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 304 | "requires": { 305 | "os-tmpdir": "~1.0.2" 306 | } 307 | }, 308 | "tslib": { 309 | "version": "1.9.3", 310 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", 311 | "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" 312 | }, 313 | "wcwidth": { 314 | "version": "1.0.1", 315 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", 316 | "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", 317 | "requires": { 318 | "defaults": "^1.0.3" 319 | } 320 | } 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker-threads-nodejs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node --experimental-worker index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "benchmark": "^2.1.4", 15 | "csv-writer": "^1.2.0", 16 | "inquirer": "^6.2.2", 17 | "ora": "^3.2.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sharedArrayBuffer/calcPrimes.js: -------------------------------------------------------------------------------- 1 | const { Worker, parentPort, workerData } = require("worker_threads"); 2 | 3 | // Get init data from mainThread 4 | const size = workerData.size; 5 | const start = workerData.start; 6 | const limit = workerData.limit; 7 | var curr = workerData.curr; 8 | const sieve = workerData.sieve; 9 | 10 | // While the current factor is less than eq limit 11 | // While the current factor is less than eq last element of thread 12 | while (curr <= limit && curr <= start + size - 1) { 13 | 14 | // if sieve isn't marked by anyone else 15 | // alter to >=0 to check performance for repeating elements 16 | if (sieve[curr - 2] == 0) { 17 | // If start is not multiple of curr, index = the extra elems needed 18 | if (start % curr) { 19 | var index = curr - (start % curr); 20 | } else { 21 | // if start is multiple of curr, index = 0 22 | var index = 0; 23 | } 24 | 25 | // loop through each index, incrementing by curr, till size of segment exhausted 26 | for (index; index < size; index += curr) { 27 | // Not changing if start+index is same as curr 28 | if (start + index != curr) { 29 | // Else altering flag to 1, as factor found 30 | // -2, since 0 indexed and first element is 2 and not 1 31 | sieve[start + index - 2] = 1; 32 | } 33 | } 34 | } 35 | 36 | // incrementing curr here => 37 | curr += 1; 38 | } 39 | 40 | parentPort.postMessage("done"); 41 | parentPort.close(); 42 | -------------------------------------------------------------------------------- /sharedArrayBuffer/index.js: -------------------------------------------------------------------------------- 1 | const os = require("os"); 2 | const path = require("path"); 3 | const { Worker, parentPort, workerData } = require("worker_threads"); 4 | 5 | const cpuCount = os.cpus().length; 6 | const workerPath = path.resolve(__dirname + "/calcPrimes.js"); 7 | 8 | const calculatePrimes = number => { 9 | return new Promise((resolve, reject) => { 10 | // constant cannot be changed through re-assignment 11 | // constant cannot be changed through re-declaration 12 | const numbers = []; 13 | 14 | // Creating a shared array buffer of size => number-1, since 1 is not counted for prime 15 | const area = new SharedArrayBuffer(number - 1); 16 | const sieve = new Int8Array(area); 17 | 18 | // When passing a whole array to worker thread, it makes a copy of the array and no changes made there 19 | // Gives a stack overflow error even for calculating upto 1000000 as a copy is made and passed to all threads 20 | // const sieve = new Array(number-1).fill(0); 21 | 22 | // Get start value and size of each segment, that is passed to the threads 23 | 24 | // ceil so as to distribute the leftout equally among threads 25 | const segmentSize = Math.ceil((number - 1) / cpuCount); 26 | var segments = []; 27 | 28 | for (let segmentIndex = 0; segmentIndex < cpuCount; segmentIndex++) { 29 | const start = segmentIndex * segmentSize; 30 | const segment = [start, segmentSize]; 31 | segments.push(segment); 32 | } 33 | 34 | // The limit till which we need to check for primes 35 | const limit = Math.ceil(Math.sqrt(number)); 36 | 37 | // initialise worker with static global data 38 | const workers = segments.map(segment => { 39 | // Create a promise for each worker 40 | return new Promise((resolve, reject) => { 41 | // creating a worker with the some inital workerData 42 | const worker = new Worker(workerPath, { 43 | workerData: { 44 | start: segment[0], 45 | size: segment[1], 46 | curr: 2, 47 | limit: limit, 48 | sieve: sieve 49 | } 50 | }); 51 | 52 | // When worker emits, message | error | exit events 53 | worker.on("message", resolve); 54 | worker.on("error", reject); 55 | worker.on("exit", code => { 56 | if (code !== 0) 57 | reject( 58 | new Error(`Worker stopped with exit code ${code}`) 59 | ); 60 | }); 61 | }); 62 | }); 63 | 64 | // When all the workers have completed processing, and returned data in order, and in sync 65 | Promise.all(workers).then(res => { 66 | // Iterating through shared buffer once, all threads have parsed, and adding elems having 0 to primes 67 | let counter = 2; 68 | const primes = []; 69 | sieve.forEach(elem => { 70 | if (elem == 0) { 71 | primes.push(counter); 72 | } 73 | counter += 1; 74 | }); 75 | 76 | // Logging number of primes, and the time to process 77 | 78 | resolve(primes.length); 79 | }); 80 | }); 81 | }; 82 | 83 | module.exports = calculatePrimes; 84 | /* 85 | TODO => 86 | 1. Analysis for SharedBufferArray v/s NormalArray 87 | 2. Recurring same process on worker thread after messaging mainThread 88 | 3. Message passing efficiency 89 | 3.1 Arrays 90 | 3.2 SharedArrayBuffers 91 | 3.3 Objects 92 | */ 93 | -------------------------------------------------------------------------------- /static/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilshah98/worker-threads-NodeJS/a05499aad4455b67e7e989aa2e9e84e900fa51cf/static/graph.png -------------------------------------------------------------------------------- /static/workerBench.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilshah98/worker-threads-NodeJS/a05499aad4455b67e7e989aa2e9e84e900fa51cf/static/workerBench.gif -------------------------------------------------------------------------------- /static/workerIndex.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilshah98/worker-threads-NodeJS/a05499aad4455b67e7e989aa2e9e84e900fa51cf/static/workerIndex.gif --------------------------------------------------------------------------------