JavaScript is often referred to as a
high level single threaded garbage
collected interpreted or just-in-time
compiled prototype-based multi-paradigm
Dynamic language with a non-blocking
event Lube but what if we could change
one of those words like single threaded
to multi-threaded believe it or not we
can actually do that thanks to workers
in node.js and the web workers API in
the browser in today's video I'll show
you how to unlock massive performance
gains by running multi-threaded
JavaScript but before we do that let's
talk about concurrency versus
parallelism these terms sound similar
and they're related but they're two very
distinct Concepts normally JavaScript is
a single threaded language which means
it can only take advantage of one CPU
core called the main thread however this
one thread can execute code concurrently
thanks to javascript's non-blocking
event Loop for example when you have a
time-consuming operation like a fetch
call to an API that code gets executed
off the main thread then when it
completes it goes back to the main
thread and executes a callback function
a callback function is named that way
for a reason it's literally like saying
execute this code then call me back when
you're done with concurrency you're
doing multiple jobs that overlap over
different time periods in no specific
order it's kind of like a restaurant
that only has one cook in the kitchen
you might have something in the stove
and in the oven and the microwave all at
the same time but when it comes to
Preparing a plate of food the chef can
only handle one at a time but now let's
talk about parallelism it's also all
about doing things at the same time but
instead it utilizes multiple CPU cores
to run code that would otherwise block
the main thread for the chef in the
restaurant it's like having extra Cooks
alongside him to prepare multiple plates
of food at the same time in practice
multi-threading is ideal when you have
multiple CPU intensive jobs going at the
same time like looping over a large
array or processing a bunch of images at
the same time would be CPU intensive
what it's not very useful for though is
i o intensive work like reading and
writing to a database the file system or
network communication because in those
cases you're waiting for the disk or the
network to supply the actual data and
the CPU can't do a whole lot to speed
that up so it's important to keep in
mind that multi-threading doesn't just
magically speed everything up it's just
a way to distribute work across multiple
CPU cores when the CPU is doing the
heavy lifting his historically people
have criticized JavaScript as a toy
language in part because it was single
threaded but it's actually been capable
of multi-threading for many years there
are libraries like Party Town that use
web workers to run scripts in the
background and that same API is also
supported in Dino but what we're going
to look at today is the workers API in
node.js to follow along simply create an
index.js file in your editor now before
we get into workers I first want to
simulate a CPU intensive task that would
block the main thread let's imagine we
have an array of jobs to simulate
something that we need to compute in
this case I'll just populate that array
with the number one billion 100 times
from there we'll create a nested for
Loop that Loops over each one of those
jobs then simply iterates from zero to
one billion until it reaches the end
totally pointless but it's good exercise
for the CPU now let's go ahead and use
the performance module to grab a
timestamp before and after this code
runs to measure the execution time now
we can go ahead and open up the terminal
run the node command and wait in my case
it took over just 40 seconds to count to
100 billion and that's a lot faster than
I could do it but not very impressive
compared to a compiled language running
on multiple threads however contrary to
popular belief JavaScript is not slow
when it's just in time compiled by V8 it
actually performs way better than many
other interpreted languages because V8
which is built with C plus compiles it
to machine code before executing it
performance will vary based on your
machine and in my case I have an Intel
I9 processor with eight physical cores
but because it does hyper threading we
technically have 16 cores to work with
just a side note a core refers to the
physical component of the CPU itself
which can only execute one task at a
time whereas a thread refers to a
sequence of instructions that can be
managed independently by the operating
system scheduler there's a distinction
to be made there but from a practical
standpoint the amount of node.js workers
you can run in parallel is equal to the
available number of CPU cores which in
this case is 16. now to keep this code
organized I'm first going to create a
new file called worker.js and we can
simply move the loop that does the
actual work into it the goal is to have
the code in this file not run on the
main thread we could actually combine
all this code in a single file because
the node worker module allows you to
check if you're are actually on the main
thread but I think it's a lot easier to
reason about when you put your worker in
a separate file and back in the main
file I'll go ahead and import the worker
class from the worker threads module
then from there I'll create a function
that takes the array of jobs as an
argument and then also specify the
number of concurrent workers or threads
that we want to run in parallel from
there I defined a helper function called
chunkify that will take the initial
array of jobs and then split it into an
equal number of subarrays so that way
each one of our workers is doing roughly
the same amount of work like if we have
a hundred jobs and four CPU cores each
worker would get 25 jobs from there we
can Loop over each one of those chunks
and then instantiate a new worker by
pointing it to the worker.js file that
we just created now here's where things
get interesting your workers can pass
messages back and forth between the main
thread after you instantiate a worker
you'll need to pass the message telling
it to get to work and that message can
also include data which in this case
would be a chunk of jobs from the
initial array now let's go back into the
worker file and see how we can receive
that message by importing the parent
port from the worker threads module we
can receive that message from the main
thread the message will contain the data
we passed in from the main thread and
allows us to execute a callback function
to process that data on a completely
separate thread which is automatically
allocated by your machine now finally
when the work is done you'll need to
notify the main thread which is
accomplished by calling post message
with this method you can pass data back
to the main thread or in this case we'll
just simply tell it that we're done
although you could just do process.exit
here if you don't want to pass any data
now the final step is to listen to
messages on the main thread from the
workers for each worker we'll set up a
listener for the message at which point
we'll know the worker has completed in
real life though you'd likely want to
collect the data from the worker and do
something with it on the main thread you
might also be wondering if threads can
share data while they run and the answer
is yes there's a channel called code
with Ryan that has a great video about
this called node.js is a serious thing
now but I'm not going to get into it in
this video because a long time ago I
tried to synchronize data across threads
in Python and I ended up with so many
race conditions that the doctors had to
amputate my leg but now it's time to
find out if this showed results in any
actual kind of performance gains the
last thing I'll do here is use the
node.js performance module to measure
the amount of time it takes for all the
workers to complete and now we're ready
to fire up the V8 engine remember that
first run on the main thread took 44
seconds so now let's go ahead and run
two workers in parallel currently my CPU
usage is at about five percent but that
should change after we run this code we
see a definite increase in utilization
up to about 15 percent and then the
total execution time was about 20
seconds roughly twice as fast as the
initial run on the main thread and
that's exactly what we would expect
because we're now using two threads
instead of one but now let's find out
what happens when we give it some more
juice with four workers as expected CPU
usage goes up to about 30 percent and
the execution time is Once Again cut in
half to about 10 seconds let's go ahead
and double it again to eight workers now
this time CPU usage goes above 50
because that's half of our available
threads and total execution time goes
down to 6.2 seconds a definite increase
but we're now starting to get some
diminishing returns now finally let's go
ahead and give it everything we've got
with 16 work workers this time the CPU
goes to 100 utilization and total
execution time comes down to 4.7 seconds
making it a total of 10 times faster
than the initial run on the main thread
now we could continue to add more
workers like say a hundred but there
will no longer be any increase in
performance because there's no available
CPU cores to use those threads
everything on the entire machine is
completely blocked now to finish up this
video I want to show you a really cool
trick related to threads while this
video is playing right now go ahead and
open up your browser Dev tools inside
the console create a while loop with a
condition of true and then inside the
brackets just go ahead and do nothing
I'll go ahead and give you a moment to
do that because it's important to run
this code while the video is playing
remember the browser is also single
threaded so when you run this code it
will lock up the main thread and the
page should become unresponsive and that
means you'll be stuck on this video
allowing me to achieve infinite watch
time I can now tell you anything I want
and you'll never be able to click away
from this video
[Music]
and I'd like to take this opportunity to
invite you to become a fireship pro
member to learn how to do all kinds of
other cool stuff with JavaScript thanks
for watching and I will see you in the
next one
if you're still here that means your
browser is probably Frozen and I
apologize for that to fix it all you
have to do is unplug your computer and
then plug it right back in or you can
right click on the browser Tab and close
it because every tab has its own thread
and if that doesn't work just leave me a
comment below the video and I'll try to
troubleshoot from there