Parallel Matrix Multiplication in Rayon Rust

Shaotong Sun

This post is written for an assignment for CSC 252 based on my tutorial given at ACM Chapter workshop titled “General Introduction of Parallel Programming Schemes in Different Languages.”

Rust, like C or C++, is a system-level programming language, but unlike C and C++, it has more features on memory safety issues and useability. In other words, it “gives you the option to control low-level details (such as memory usage) without all the hassle traditionally associated with such control.”1

For installation of the Rust, please refer to https://doc.rust-lang.org/book/ch01-01-installation.html.

Ownership in Rust

As mentioned above, Rust language is designed to focus on memory safety issues. To this end, Rust uses something called Ownership. “Ownership is Rust’s most unique feature and has deep implications for the rest of the language. It enables Rust to make memory safety guarantees without needing a garbage collector.”2 On the highest level, ownership means that some variable owns some value, and the can only be one owner for a value at a time. Specifically, the Rust Book defines the ownership rules as:

Ownership Rules

  • Each value in Rust has an owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.
https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html

This ownership concept not only solves the memory safety issues but also makes writing concurrent programs much more accessible than expected. Having ownership makes issues such as data race compile-time errors rather than runtime errors in many cases.

Rayon in Rust

Like OpenMP and OpenCilk in C and C++, Rayon is a data-parallelism library in Rust that helps you write parallel code safely and quickly, which eases you from manual manipulation of threads.

There are two ways to use Rayon:

  • High-level parallel constructs are the simplest way to use Rayon and also typically the most efficient.
    • Parallel iterators make it easy to convert a sequential iterator to execute in parallel.
    • The par_sort method sorts &mut [T] slices (or vectors) in parallel.
    • par_extend can be used to efficiently grow collections with items produced by a parallel iterator.
  • Custom tasks let you divide your work into parallel tasks yourself.
    • join is used to subdivide a task into two pieces.
    • scope creates a scope within which you can create any number of parallel tasks.
    • ThreadPoolBuilder can be used to create your own thread pools or customize the global one.
https://docs.rs/rayon/latest/rayon/

This tutorial will only cover the most basic method of parallelizing matrix multiplication using Rayon, which is using parallel iterators (more can be found at: https://docs.rs/rayon/latest/rayon/).

The most naive way of matrix multiplication in Rust is shown below:

fn seq_mm(a: &[f64], b: &[f64], c: &mut [f64], n: usize) {
    for i in 0..n {
        for j in 0..n {
            for k in 0..n {
                c[i * n + j] += a[i * n + k] * b[k * n + j];
            }
        }
    }
}

By using par_chunks_mut, we can divide the matrix c into n distinct, separate sections, with each section smaller than or equal to n. Rayon then automatically processes these sections in parallel for us.

fn par_mm(a: &[f64], b: &[f64], c: &mut [f64], n: usize) {
    c.par_chunks_mut(n)
        .enumerate()
        .for_each(|(i, c_row)| {
            for j in 0..n {
                for k in 0..n {
                    c_row[j] += a[i * n + k] * b[k * n + j];
                }
            }
        });
}

With n=1000 and no other optimizations (meaning cargo run), the sequence matrix multiplication takes around 7 seconds to finish, while the parallel matrix multiplication takes only around 2 seconds.

Conclusion

In conclusion, Rayon in Rust seems to be beginner-friendly and easy to use, producing a relatively good performance without much work. If you are interested in Rayon or Rust, be sure to check out the Rust Book.

  1. https://doc.rust-lang.org/book/ch00-00-introduction.html ↩︎
  2. https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html ↩︎

Leave a comment