[No Audio]
Performance is important for many Rust
programs. This section of the course contains
many techniques that can improve the
performance in terms of speed and memory
usage. Some techniques are entirely Rust
specific, and some involves ideas that can be
applied with modifications to programs
written in other languages. In this first
tutorial, we will be talking about
benchmarking. Benchmarking typically involves
comparing the performance of two or more
programs that do the same thing or same task.
Sometimes this might involve comparing two
different implementations, and at other times,
it may involve comparing two different
versions of the same implementation. The key
question that we would like to answer in this
context is, did the change speed up things or
not, and benchmarking different factors may
be taken into consideration such as
processing time, memory usage, and disk
access time. However, in this tutorial, we
will be primarily focusing on processing
time. Let us look at an example for
understanding the benchmarking in Rust.
Consider a scenario where we would like to
compare two implementations of sorting
elements of an array. In the lib.rs file, I
have added these two implementations as
functions. The first implementation is
sorting algo_1, and the second
implementation is sorting algo_2. These two
functions are representing two different
implementations of sorting algorithms.
The input to both the functions are a mutable
reference to a vector, and inside the
function, these functions are sorting the
respective vector elements. Now, let us see
how we can use benchmarking in order to track
performance and make sure that performance is
not regressed. Rust actually has a built in
benchmark tests. However, at this moment,
they are unstable features and are therefore
only available in nightly versions of Rust.
So instead, we are going to use a library
called criterion. To properly use the library,
we will open the cargo.toml file.
We will add Criterion as dev dependency.
[No Audio]
Next, we will configure our benchmark target, which is
the name of the file that contains our
benchmark tests. I will use the name of
sorting_benchmark.
[No Audio]
Next we will set harness to false.
[No Audio]
This will disable the
default benchmarking system, so we could use
Criterion's provided benchmarking system. Next,
we will create a folder called benches in the
root of our project.
[No Audio]
Please make sure that the
folder name must be benches, otherwise,
the command for executing the benchmark may
not be successful as cargo may not be able to
find the relevant file for the benchmark.
Inside that folder, we will create a new file
called sorting_benchmark.rs.
[No Audio]
This name must be the same as the name that we
mentioned in the cargo.toml file.
Now we will add some code to the file. First let's
let's bring our sought array functions into scope.
[No Audio]
We will also bring some items from the
Criterion crate into scope.
[No Audio]
Next, we will set up our benchmarking test function.
[No Audio]
The benchmark function gets access to an instance
of the Criterion struct, which allows us to
configure and execute benchmarks. In this
case, the variable c is a mutable reference
of type Criterion. We will use it to create a
new benchmark. Inside the function, I will
first define a vector containing some numbers.
[No Audio]
The vector contains a list of
unsorted numbers which will be sorted using
the sorting algorithm, that we will be using
for benchmarking, and now let's create our
benchmark. To create a new benchmark we will
call the benchmark function on the
Criterion struct instance.
[No Audio]
This function takes two
arguments, an id, which I will mention inside quotes.
[No Audio]
And the second argument of a closure.
Inside the closure, we get access to an
instance of Bencher which allows us to
iterate a benchmark function to measure its
performance. Inside the closure body we will
call the iter method.
[No Audio]
This takes another closure
that executes our sort function.
[No Audio]
The sort array function will execute many times
for its performance to be accurately
measured, and with that our benchmark is now
complete. Next, we will use the criterion_group macro.
[No Audio]
This macro is used to define a
collection of functions to call within a
common Criterion configuration. The first
argument is the name of the group, which in
this case is benches and the remaining
arguments or names of functions. Since we have
only a single function, so we will mention that.
[No Audio]
Finally, we will need to use one macro
called a criterion_main.
[No Audio]
This macro expands to
a main function which runs all the benchmarks
in a given group, in this case, the benches
group. We are all set up, so let's go ahead and
rent a benchmark tests. To execute, we will use
the command of cargo bench.
[No Audio]
It provides information about 100 runs of the
same program and compute their average
running performance in terms of nanoseconds.
In this case, the average running times turns
out to be this much nanoseconds. It also
shows in some information about the outliers,
which out of the 100 runs were quite deviant
in performance. Let us run the benchmark again.
[No Audio]
It says this time no change in
performance is detected. This means that the
performance is quite stable and does not
change during different runs. Now, let us
change the algorithm from algorithm 1 to
date of algorithm 1 and see the performance again.
[No Audio]
The performance has regressed. This clearly
indicates that the performance of algorithm 1
is superior to that of algorithm 2. It
also gives useful information about the
percentage change in the performance. In this
case, it is quite high, and therefore provides
a clear indication to the implementers that
they should prefer to use algorithm 1 over
algorithm 2. That brings us to the end of
this tutorial. See you again with more
performance improvement tutorials and until
then happy Rust programming.