Using Web Workers to run JavaScript in parallel

JavaScript, in the browser, runs in a single thread. This is fine, and works fairly well for most websites. However, when running large, complex tasks or long scripts, it makes the webpage unresponsive.

The best way to speed up these tasks is by running code in separate threads. This has been doable for some time, using ArrayBuffers, but it has considerable memory overhead.

An experimental feature, SharedArrayBuffer, provides a way to effectively share data, using Atomics, between threads.

 How do we achieve this?

Web Workers provide a way to run code in seperate threads. These threads can run parallel to other threads, and ensure that our page doesn’t freeze up while processing.

The downfall of Web Workers is that they can’t access the DOM or variables in the main thread, but it can make XHR requests.

This means that we need to somehow communicate results of the worker threads back to the main thread to be presented to the DOM.

 Example time

I’ve prepared three examples in JSFiddle with benchmarks for each—best used in Chrome.

 No parallel code

 Parallel code using ArrayBuffer

 Parallel code using ArrayBuffer & 4 workers

 Parallel code using SharedArrayBuffer & 4 workers

SharedArrayBuffer only works in Firefox 46 and above, and in Chrome.

[1] This feature is disabled by a preference setting. In about:config, set javascript.options.shared_memory to true.
2] The implementation is under development and needs these runtime flags: --js-flags=--harmony-sharedarraybuffer --enable-blink-feature=SharedArrayBuffer

 Observations

 Cloning arrays between more than one worker is difficult

I kept getting this piece wrong, and it took forever to finally get it to accurately clone the array and return the chunk with the correct offset.

 Cloning uses way more memory than expected

Dealing with a 2MB ArrayBuffer should have only used around 6MBs of memory - but resulted in 10MBs total usage. This may be because 2MBs is used with the initial ArrayBuffer creation, cloning costs another 2MBs, copying the clone to the worker costs another 2MBs, the worker clones the copy costing yet another 2MBs and then the worker moves the copy back to the main thread at a low low price of another 2MBs. Totalling 10MBs of stuff.

If you want to observe the weirdness of the cloning - check out the call stack.

 postMessage uses less memory because we don’t have to clone

postMessage is a “transfer list” and can hold a list of Transferable objects that will just be transferred instead of cloned. The idea behind this is to use less memory and to not clone things pointlessly.

When running the test, about a maximum of 4MBs memory was used.

https://developer.mozilla.org/en-US/docs/Web/API/Transferable

 Using too many workers will actually be slower

Keep in mind that most systems have a certain number of cores / threads. If you spawn more workers than there are threads, you’ll actually slow down the workload as it will need to queue the workers.

 This doesn’t test against race conditions

By breaking up the ArrayBuffer with an offset, each worker has its own part of the “memory” to write to. More complex structures could easily overwrite the same part of the memory - oops.

SharedArrayBuffer works with Atomic operations (see Mozilla’s proposal) to resolve this issue.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics

 To conclude

JavaScript is single-threaded and freezes up the DOM when running complex operations. You can dodge this using WebWorkers.

SharedArrayBuffer and Atomics provide an exciting future for multiple threads and shared memory in the browser.

 
21
Kudos
 
21
Kudos

Now read this

Another post by somebody who left a popular blogging platform

Gosh. Shock horror. I left Medium.com. I guess, it was an easy decision after they stopped their custom domain approval process for a “short review”. Psst! Still waiting, guys! I’ve been eager to find something better, so I’ve decided to... Continue →