Simple Implemention to Understand worker_threads in NodeJS
One of the most highly request features in the NodeJS runtime environment is being able to use multiple cores — a lot of programmers don't exactly understand the availability of NodeJS packages available to use to help with this issue. The package is worker_threads
. This package really does help fix the issue.
Right below is an everyday code example without using worker_threads
.
const now = Date.now();
const arr = [];
for(let i = 0; i < 10000000; i++){
arr[i] = Math.random()
}
const createArrayTime = Date.now() - now;
let sum = 0;
let numberBefore = 1;
for(let i = 0; i < arr.length; i++){
sum = sum + arr[i] / numberBefore;
numberBefore = arr[i];
}
const addArrayTime = Date.now() - now;
console.log(createArrayTime, addArrayTime)
Breaking Down the Example
- The code starts with creating an array with 10 million random numbers, it calculates the time for doing this.
- It then loops the array and adds all numbers together, but each number added is divided by the number before. This is done with the
numberBefore
variable, it calculates the time for doing all of this. - The code then logs to console the time it took in milliseconds.
The Results
> node index.js
296 500
> exit
With us running the code, it only took approximately 800 milliseconds to run. For such a simple application, that is quite slow. We are able to speed up this application code to just an average of 100 milliseconds. This is a major performance gain with some code refactoring.
What is worker_threads
and how do you use it?
worker_threads
allows you to send what code each thread of a CPU core should run.- The
workers
(each thread) are able to communicate with each other and the main code. - They can also be used to run multiple files at the same time.
Learning how to properly use worker_threads
could be an extremely important tool for building anything with NodeJS due to the benefits.
How do I use worker_threads
?
Start with the basics, declaring your imports.
import { parentPort, isMainThread, Worker }
from 'node:worker_threads';
import os from 'node:os';
Bringing up the issues we had before with performance we had earlier, the goal is to speed up and make the small application of code above to simply run faster. Since as we all know NodeJS runs everything on a single core – this is by default also, we can change this behavior to use multi-threading.
Bringing our array back without the use of worker_threads
to create it will be more clear. Here is a code snippet of how we are going to do this.
const arr = [];
for(let i = 0; i < 10000000; i++){
arr[i] = Math.random()
}
Now that we have an input, we need to split the input into different blocks. This is because the worker_threads
module does not automatically assign a task to each thread and we have to assign it ourselves. This gives more functionality.
function chunkify(array, amnt){
const chunks = [];
const chunkSize = Math.ceil(array.length, amnt);
for(let i = 0; i < array.length; i++){
chunks.push(array.slice(i, i + chunkSize);
}
}
We now can use both the os
and worker_threads
to make the most out of our code. Since we have the function needed to split our input, we are able to make each thread run a certain chunk of the code.
We can use isMainThread
to identify whether or not we are running as a worker
or a MainThread
.
if(isMainThread){
// stuff...
let results = [];
async function run(chunks){
const promises = [];
for(let i = 0; i < chunks.length; i++){
const worker = new Worker('./index.js');
const promise = new Promise((resolve, reject) => {
worker.on('message', (message) => {
resolve(message);
results.push(message);
});
worker.postMessage(chunks[i]);
});
promises.push(promise);
}
await Promise.all(promises);
}
run(chunks, os.cpus().length - 1); // Leave 1 core.
} else {
// ...
We now have a run
function to use for workers.
- The code will re-run
index.js
, but not as theMainThread
, but rather the worker. - The code will then execute everything in the
else
block, this will allow the split inputs to be executed by each thread. - It will then resolve the promise, which will then add
message
(a message from the worker to the Main Thread) to aresult
variable.
Now, in the worker
code, we need to learn how to recieve and send messages from each worker.
And voila! Now we have our code working with multiple threads in JavaScript! The results of these code changes are right below.
The Analysis
The results are as to be expected. The first number has not changed since we did not use any workers
to make our input, while the second number is just the time of the code from when the workers were just being executed to the end. This excludes using functions like chunkify
or with os
grabbing CPU counts.
The Conclusion
JavaScript Node.js is one of the most powerful coding languages and environments in the world for backend applications. Being able to use worker_threads
and seeing amazing results from them really makes it even better.
Do you like what you're reading from the CoderOasis Technology Blog? We recommend reading our Implementing RSA in Python from Scratch series next.
The CoderOasis Community
Did you know we have a Community Forums and Discord Server? which we invite everyone to join us? Want to discuss this article with other members of our community? Want to join a laid back place to chill and discuss topics like programming, cybersecurity, web development, and Linux? Consider joining us today!