├── .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
--------------------------------------------------------------------------------