Let's take a moment to look at a crate that we can use to help us parallelize our programs.
Rayon is a lightweight data parallel laser crate that makes it super easy to convert sequential computations
into parallel computations while guaranteeing safety from data races.
A unique characteristic that Rayon provides is that it will automatically spawn our threads for us.
But how does it know how many to spawn?
Well, by default, Rayon uses the same number of threads as the number of logical CPUs available.
Assuming Hyper-threading is enabled.
Otherwise, it will just use the number of physical CPUs.
So let's do a quick example of creating a program that computes the factorial of a number and compare
the performance between single threaded and multithreaded.
So the first thing we need to do in our cargo file is add a couple of dependencies and I've already
added them.
And so we need Ray on a version one and then obviously it'll take our latest version and then also NUM
and the latest version under 0.4.
And then inside Main, we can start importing in these dependencies.
So we'll go ahead and get them all imported in.
So we'll start out with Ray on Prelude and then Asterisk to take everything in and now we'll use NUM
and we want big unsigned INT and one.
And then lastly, we want standard time and instant.
And this is going to be what we use to.
Measure the time it takes for our functions to run.
So we'll start off with creating our single threaded factorial function, and it's going to accept one
parameter of an unsigned 32 bit integer, and we're going to resign a big unsigned integer.
And the reason we have to use a big unsigned integer is because if we used the largest unsigned integer
that Russell provides us, which is a unsigned 128, our factorial would actually only be able to go
up to 35.
I found that out by just playing around with it a little bit.
So we needed to use a big we need to use a big unsigned integer.
That way we can use much larger values when trying to compute our factorial.
So when computing a factorial, we need to do a couple of checks.
We need a check if the number passed in is equal to zero or if it equals to one.
And if it does, then we want to return.
A big unsigned integer one.
Otherwise now we can actually compute our factorial and so we'll do from one to num, including our
number.
We want to map that value, that element into a big unsigned integer.
And then we want to call the reduced method on this.
And I will explain to you what reduces in a second.
I just want to get this set up.
That way we can look at it.
And what do we have here?
No function.
How could this need to be capitalized?
There we go.
All right, so let's let's break down this code.
So again, we created one to NUM, which is also inclusive, since we have this equal sign and then
we map it into a big unsigned integer.
And then we call the reduced function on it.
And the reducing function is a closure with two arguments.
So we have argument one here and argument to here and argument one is going to be the accumulator and
then X is also is going to be the element that we're passing in from here.
So what the accumulator is going to do is we're going to say, Hey, take the accumulator and then multiply
it by X, and then now that's our accumulator, and then just continuously do this until we equal NUM
and then reduce returns and option.
So we have to unwrap it in order to get the value inside of it.
So let's create a test program of this or a simple test just to make sure that it is working correctly.
And the factorial of three is six.
So if this runs correctly, we should see six.
Which is what we have printed out so great.
So we know that it's working correctly, but just to verify.
Let's also do four and we see 24.
So awesome.
So everything's working properly.
So now let's take this function and let's use rayon.
That way we can have the code running in parallel.
So we'll just call it multi factor multithreaded factorial and our setup is going to be extremely similar.
So we're going to take in an unsigned 32 bit integer and now we're going to also return a big unsigned
integer.
So let's do our check again
return.
One and then otherwise.
We want one to numb, inclusive.
And now where rayon comes in is we're going to call in pas etre and this is going to create an end to
iterator for us.
But PAS stands for parallel, so we're going to create a parallel iterator and now we're going to map
big unit from like we did above.
And now we're going to call reduce again.
And before I fill in what reduces, let's look at the function signature up here.
And we see that we have a disappeared, a mute, mutable self and then F of type F.
Well, if we look at this reduced function, we actually see the function signatures different.
So we see that we're going to take two parameters in this case.
So we need to modify it that way.
And then this is going to allow us for parallel execution.
So this reduced function actually comes inside the Rayon Library.
So let's set up this reduced function to allow it to be happy.
So we're going to have a big unit of one.
We're going to have our accumulator.
We're going to have our X value the same as above.
And then we're going to have accumulator times X.
So what reduces and let's see if it gives us a good example here.
So we have to include an identity parameter, which is what we're doing with big unit and then one.
And then the identity iterator is going to be where?
We start out at.
So we'll have one and that's where our accumulator is going to start at.
So it's just a different function signature provided to us by the Reon Crate to essentially do this
up here.
It's just typed out slightly different and in this case it's just going to return our item to us so
we do not have to unwrap this.
So now let's look at this and make sure that our factory rules are computing the same value.
So we see 24 and both.
And then just to make sure, let's do three again and make sure each one prints out six.
So great.
So both of our factorial functions are printing out the correct values as we expect.
So now let's create a benchmark for this.
And this is where our time incident comes into play.
So now we're going to create an instant and assign it now.
And now we're going to calculate our factorial of 50,000.
And now we want to print out how much time it takes for that computation.
So we'll give it a little bit of styling.
So we want it to print out.
Two decimals into the second as seconds are just two decimals and we'll say now dot elapsed to get the
time differentiation so from time now to time.
Now here as well and then the difference between the two so the time elapsed and now we want to do this
again but we want to do it with our multi factorial.
And so if we run this now, the factorial of 50,000 single threaded is going to take 4.9 2 seconds and
our paralyzed factorial actually took 169 and a half milliseconds.
So that just shows you how much faster it is to run programs in a parallel manner.
So if we run it again and this time 40,000, we see that we're seeing magnitudes higher of performance
speed ups, which is why I wanted to show you just how awesome this real crate is.
So hopefully this was a very insightful lecture for you into the power that parallel ising our applications
can provide for us.