Parallel Tasks
Add the rayon
crate to your own project:
cargo add rayon
Other crates needed for the example code:
cargo add glob rand image
Mutate the elements of an array in parallel
The example uses the rayon
crate, which is a data parallelism library for Rust.
rayon
provides the par_iter_mut
method for any parallel iterable data type.
This is an iterator-like chain that potentially executes in parallel.
use rayon::prelude::*; fn main() { let mut arr = [0, 7, 9, 11]; arr.par_iter_mut().for_each(|p| *p -= 1); println!("{:?}", arr); }
Test in parallel if any or all elements of a collection match a given predicate
This example demonstrates using the rayon::any
and rayon::all
methods, which are parallelized counterparts to std::any
and std::all
. rayon::any
checks in parallel whether any element of the iterator matches the predicate, and returns as soon as one is found. rayon::all
checks in parallel whether all elements of the iterator match the predicate, and returns as soon as a non-matching element is found.
use rayon::prelude::*; fn main() { let mut vec = vec![2, 4, 6, 8]; assert!(!vec.par_iter().any(|n| (*n % 2) != 0)); assert!(vec.par_iter().all(|n| (*n % 2) == 0)); assert!(!vec.par_iter().any(|n| *n > 8 )); assert!(vec.par_iter().all(|n| *n <= 8 )); vec.push(9); assert!(vec.par_iter().any(|n| (*n % 2) != 0)); assert!(!vec.par_iter().all(|n| (*n % 2) == 0)); assert!(vec.par_iter().any(|n| *n > 8 )); assert!(!vec.par_iter().all(|n| *n <= 8 )); }
Search items using given predicate in parallel
For this example, add the rand
crate to your own project:
cargo add rand
This example uses rayon::find_any
and par_iter
to search a vector in
parallel for an element satisfying the predicate in the given closure.
If there are multiple elements satisfying the predicate defined in the closure
argument of rayon::find_any
, rayon
returns the first one found, not
necessarily the first one.
Also note that the argument to the closure is a reference to a reference
(&&x
). See the discussion on std::find
for additional details.
#![allow(unused)] fn main() { {{#include examples/rayon-parallel-search.rs} }
Sort a vector in parallel
This example will sort in parallel a vector of Strings.
Allocate a vector of empty Strings. par_iter_mut().for_each()
populates random
values in parallel. Although multiple options
exist to sort an enumerable data type, par_sort_unstable
is usually faster than stable sorting algorithms.
use std::str::from_utf8; use rand::{Rng, thread_rng}; use rand::distributions::Alphanumeric; use rayon::prelude::*; // par_iter_mut(), par_sort_unstable() // Generate a random word, save it to the given string. fn generate_random_word(word: &mut String) { let word_length = 5; let mut rng = thread_rng(); // Generate 5 bytes in alphanumeric range. let mut alpha_bytes = vec![0; word_length]; for i in 0 .. word_length { alpha_bytes[i] = rng.sample(&Alphanumeric); } *word = from_utf8(&alpha_bytes).unwrap().to_string(); } // Generate random words in parallel, then sort them in parallel. fn main() { let word_count = 10; // try increasing to 100000 or more let mut vec = vec![String::new(); word_count]; // replace with vec.iter_mut() for single threaded vec.par_iter_mut().for_each(generate_random_word); // replace with vec.sort_unstable() for single-threaded vec.par_sort_unstable(); for word in vec { println!("{word}"); } }
To truly exercise multiple threads of execution on multiple processor cores,
change word_count
to be 100,000 or larger. Compare the execution time and CPU load
versus the single threaded versions of the same vector operations.
If the Rust Cookbook has been cloned, run:
cargo run --example rayon-parallel-sort
Map-reduce in parallel
This example uses rayon::filter
, rayon::map
, and rayon::reduce
to calculate the average age of Person
objects whose age is over 30.
rayon::filter
returns elements from a collection that satisfy the given
predicate. rayon::map
performs an operation on every element, creating a
new iteration, and rayon::reduce
performs an operation given the previous
reduction and the current element. Also shows use of rayon::sum
,
which has the same result as the reduce operation in this example.
use rayon::prelude::*; struct Person { age: u32, } fn main() { let v: Vec<Person> = vec![ Person { age: 23 }, Person { age: 19 }, Person { age: 42 }, Person { age: 17 }, Person { age: 17 }, Person { age: 31 }, Person { age: 30 }, ]; let num_over_30 = v.par_iter().filter(|&x| x.age > 30).count() as f32; let sum_over_30 = v.par_iter() .map(|x| x.age) .filter(|&x| x > 30) .reduce(|| 0, |x, y| x + y); let alt_sum_30: u32 = v.par_iter() .map(|x| x.age) .filter(|&x| x > 30) .sum(); let avg_over_30 = sum_over_30 as f32 / num_over_30; let alt_avg_over_30 = alt_sum_30 as f32/ num_over_30; assert!((avg_over_30 - alt_avg_over_30).abs() < std::f32::EPSILON); println!("The average age of people older than 30 is {}", avg_over_30); }
Generate jpg thumbnails in parallel
Add the image crate to your own project:
cargo add image
This example generates thumbnails for all .jpg files in the current directory
then saves them in a new folder called thumbnails
.
glob::glob_with
finds jpeg files in current directory. rayon
resizes
images in parallel using par_iter
calling DynamicImage::resize
.
use std::error::Error; use std::path::Path; use std::process::exit; use std::fs::create_dir_all; use glob::{glob_with, MatchOptions}; use image::imageops::FilterType; use rayon::prelude::*; fn main() -> Result<(), Box<dyn Error>> { let options: MatchOptions = Default::default(); let files: Vec<_> = glob_with("*.jpg", options)? .filter_map(|x| x.ok()) .collect(); if files.len() == 0 { println!("No .jpg files found in current directory"); exit(1); } let thumb_dir = "thumbnails"; create_dir_all(thumb_dir)?; println!("Saving {} thumbnails into '{}'...", files.len(), thumb_dir); let image_failures: Vec<_> = files .par_iter() .map(|path| { make_thumbnail(path, thumb_dir, 100) .map_err(|e| println!("{e}: path: {}", path.display())) }) .filter_map(|x| x.err()) .collect(); println!("{} thumbnails saved successfully", files.len() - image_failures.len()); Ok(()) } fn make_thumbnail<PA, PB>(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<(), Box<dyn Error>> where PA: AsRef<Path>, PB: AsRef<Path>, { let img = image::open(original.as_ref())?; let file_path = thumb_dir.as_ref().join(original); Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest) .save(file_path)?) }