0

I have a function, that gets an array, start index and end index. I want to return the largest element of this array. Sequentially it works fine. But I don't know how to convert it to work in prallel. So can you show me how to do it. I've been trying Rayon's for_each() but always got some errors. How would converted function look like?

pub fn search_max(array: &[f32], start: i32, end: i32)-> f32 {
    let mut maximum: f32 = A[p as usize];
    let iter: usize = start as usize + 1;
    for iter in iter..end as usize{
        if maximum < array[iter] {
            maximum = array[iter];
        }
    }   
    maximum
}
mm mm
  • 19
  • 2

1 Answers1

2

You've currently got what's called imperative code; it cannot be directly changed into parallel without going through a functional style:

pub fn search_max(array: &[f32], start: i32, end: i32) -> f32 {
    let sub_array: &[f32] = &array[start as usize..end as usize];
    *sub_array.iter().max().unwrap()
}

We use the max method to get the maximum value produced by the iterator. This does not work since f32 cannot be guaranteed to order nicely. We'll use max_by instead with a closure:

pub fn search_max(array: &[f32], start: i32, end: i32) -> f32 {
    let sub_array: &[f32] = &array[start as usize..end as usize];

    *    // This results in &f32, so we must dereference it
    sub_array.iter() // Create the iterator over references to the items
    .max_by(
        |x, y| x.partial_cmp(y).unwrap() // https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#tymethod.partial_cmp
    ).unwrap() // There could possibly be no items! So we get an `Option<&f32>` instead.
}

For the most part, rayon's ParallelIterator is a drop in replacement for a regular iterator, so anywhere we see .iter we change to .par_iter:

use rayon::prelude::*;

pub fn search_max(array: &[f32], start: i32, end: i32) -> f32 {
    let sub_array: &[f32] = &array[start as usize..end as usize];
    *sub_array.par_iter().max_by(|x, y| x.partial_cmp(y).unwrap()).unwrap()
}

Playground


If you're looking for the most idiomatic version of this:

pub fn search_max(slice: &'_ [f32], start: usize, end: usize) -> f32 {
    *slice[start..end].par_iter().reduce(
        || &slice[start], // This is an iterator over &'_ f32
        |a, b| if a > b { a } else { b },
    )
}

Playground

Optimistic Peach
  • 3,862
  • 2
  • 18
  • 29
  • 2
    For the "most idiomatic" version, you probably wouldn't pass `start` and `end` as parameters since the caller can always do `search_max (slice[start..end])` herself if she doesn't want to search the whole slice. – Jmb Dec 05 '19 at 07:37
  • 1
    Or accept [`impl RangeBounds`](https://doc.rust-lang.org/std/ops/trait.RangeBounds.html) – hellow Dec 05 '19 at 08:43
  • @Jmb I wanted to preserve the caller side for the most part; personally I'd not have this function like so, as though parallelism seems a bit too much for a simple max/min implementation. – Optimistic Peach Dec 05 '19 at 23:39