Parallel operations in Node.js (Thread Pool)
Tags: javascript, node, learning
Published: 2024-01-25 14:47:20

Recently I had an Interview where I got the question "How many async operations can node js handle simultaneously?" the interviewer expected to hear 4, however, my answer was that for me it is 12 cause I have 12 logic threads in my CPU. My answer wasn't welcomed so let's do a little experiment to prove that my answer was correct!

We begin with a simple hash operation that can be performed by using the inbuilt `node:crypto` module, hashing is a perfect fit since it requires some CPU capacity. For time measurements I will use a difference of dates, it is not the best option but simple enough to see the correlation.

```javascript
import crypto from 'node:crypto';

const start = Date.now();

crypto.pbkdf2Sync("password", "salt", 100000, 512, 'sha512')
console.log('Hash: ', Date.now() - start)
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ket4diawodtki8xbfczf.png)

We see that one sync hash operation takes about 319ms, from here we can guess that the second log will be at about 640ms, the third 960ms etc. And if we change the code we get the result that proves it: 

```javascript
import crypto from 'node:crypto';

const start = Date.now();

crypto.pbkdf2Sync("password", "salt", 100000, 512, 'sha512')
console.log('Hash: ', Date.now() - start)
crypto.pbkdf2Sync("password", "salt", 100000, 512, 'sha512')
console.log('Hash: ', Date.now() - start)
crypto.pbkdf2Sync("password", "salt", 100000, 512, 'sha512')
console.log('Hash: ', Date.now() - start)
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v3bqssntv1mcxec18xfp.png)

This simple diagram parents how this looks in graph format:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ppvasdy73tfi3v75i65.png)

Ok, now that we have an understanding of sync behaviour let's switch to the async and measure its limits. As we know JavaScript is a single-threaded language, however NodeJS has not only the EventLoop but also a thread pool that is handled by libuv. So the question how many threads are available to handle the required operations? 

I changed our example slightly to have more control over hashing calls amount and used `pbkdf2` which is async and accepts a callback. 

```javascript
import crypto from "node:crypto";

const CALLS_AMOUNT = 4;
const start = Date.now();

for (let i = 0; i < CALLS_AMOUNT; i++) {
  crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
    console.log(`Hash ${i+1}: `, Date.now() - start);
  });
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/atrio90lhynb5g7sy08b.png)

As we can see the difference in time is not that big so we can conclude that all operations executed simultaneously, the diagram shows this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdgel4z6i0zg6ctshccy.png)

Let's double the number of transactions:

```javascript
const CALLS_AMOUNT = 8;
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z9pxch5he08dm0fboaw2.png)

As we can see first four calls are executed simultaneously then executed next four, on the diagram this looks like:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/quvaaa1i5z59d2hk0c4v.png)

_From this point, many may conclude that in NodeJS we can perform 4 asynchronous operations at the same time, but is this really the case?_

Here I need to remark that the Thread Pool has size and by default this size equals 4, though we can control its size it still has limitations like logic threads of your CPU. Size can be controlled using `UV_THREADPOOL_SIZE` environment variable like:

```javascript
import crypto from "node:crypto";

process.env.UV_THREADPOOL_SIZE = 12

const CALLS_AMOUNT = 12;
const start = Date.now();

for (let i = 0; i < CALLS_AMOUNT; i++) {
  crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
    console.log(`Hash ${i+1}: `, Date.now() - start);
  });
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nflewc67r1nezgrfnzql.png)

I set the `UV_THREADPOOL_SIZE` to 12 because my PC has 6 cores and 12 threads. In case I set the value over the 12, next operations will be executed it next time interval e.g. for 16 calls: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fol78i3r6lut1ju0dxud.png)

---

## Conclusion 

The in NodeJS the amount of async or I/O operations that can be performed simultaneously depends on the Thread Pool size that is limited by your CPU threads