added threads in js post

This commit is contained in:
2022-08-25 15:42:29 +01:00
parent ed9fb5dc4c
commit 49b6488605

View File

@ -0,0 +1,129 @@
---
layout: '../../layouts/post.astro'
title: 'Threaded JavaScript!'
---
# Threaded Javascript
For a long time, my language of choice has been JavaScript (and recently TypeScript), this is for both frontend development as well as backend development.
I enjoy the simplicity of using the same language for both sides of a project and I especially like working with Node.js, a Javascript run time environment with a very sophisticated asynchronous event loop, which makes it perfect for applications such as RESTful APIs which access databases etc...
However, I have also done backend work using Golang, a strongly typed, compiled, very fast language with a vibrant community and a "do it yourself" attitude, quite the change from Javascript. And something I have touched and enjoyed playing with are go routines, which are a way to create very light weight threads. This works by calling a function as a "go routine", which I think is very elegant. This is managed by the go run-time environment, but it can spawn threads if it needs to - a nice abstraction which means I don't have to handle threads directly - but do have to handle go routines as if they ARE threads.
But anyways, it left me wondering if I could have threads in JavaScript, which sounds crazy but Node.js has support for this is "Worker Threads". These are threads managed by Node.js which can run a .js file and communicate with its parent - it is quite similar to go routines.
So I made a small program to calculate the same MD5 hash 1 million times:
(Note that these files are .mjs files (module JavaScript), Node.js will only run them if you have .mjs as the file extension - All they do is allow the "import" syntax and a few other things).
```js
import crypto from 'crypto';
console.time("Single-Thread");
const n = 10000000;
const string = "Hello World";
for (let i = 0; i < n; i++) {
const hash = crypto.createHash('md5').update(string).digest('hex');
}
console.timeEnd("Single-Thread");
```
Running this program gave me this result:
```
Single-Thread: 1.139s
```
I then modified my code to use worker threads. This splits the work load into 8 worker threads, it gives each thread a start and an end range for the hash - which in total makes 1 million hashes.
```js
import { fileURLToPath } from 'url';
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
import crypto from 'crypto';
const __filename = fileURLToPath(import.meta.url);
const n = 1000000;
const THREAD_NUM = 8;
const string = "Hello World";
if (isMainThread) {
const threads = new Set();
for (let i = 0; i < THREAD_NUM; i++) {
threads.add(new Worker(__filename, {workerData: {
start: i * (n / THREAD_NUM),
end: (i * (n / THREAD_NUM)) + n / THREAD_NUM}
}))
}
console.time("Thread");
for (const worker of threads) {
worker.on('message', msg => console.log(msg));
worker.on('exit', () => {
threads.delete(worker);
if (threads.size === 0) {
console.timeEnd("Thread");
}
});
}
} else {
for (let i = workerData.start; i < workerData.end; i++) {
const hash = crypto.createHash('md5').update(string).digest('hex');
}
parentPort.postMessage('Done!');
}
```
This results in the following output:
```js
Done!
Done!
Done!
Done!
Done!
Done!
Done!
Done!
Thread: 335.294ms
```
A 4x increase in speed. There is a delay because worker threads actually spawn threads and therefore there is a bit of overhead with the syscall to create these threads.
This is very clear if we changed the number of hashes to 10 000.
### Single Thread
```
Single-Thread: 23.476ms
```
### 8 Worker Threads
```
Done!
Done!
Done!
Done!
Done!
Done!
Done!
Done!
Thread: 70.337ms
```
As you can see the single thread beat the 8 threads by almost 4x. This is because it is costly to create a thread outright which is what Node.js seems to be doing.
## Disclaimer
The Node.js crypto module is basically a wrapper for the native OpenSSL hashing functions, so I suspect that Node.js is just making a very fast C program do the hard work - but never the less it works very well.
## Conclusion
Node.js is a very solid backend technology to perform parallalism, it is not just limited to making RESTful APIs. It can competes with strongly typed and compiled languages such as golang.