0

I'm novice to Rust (v1.0.0) and thread-programming. I try to calculate elements of b-array using a-array. Each element of the b-array can be calculated independently of the others (parallel).

extern crate rand;
use rand::Rng;
use std::io;
use std::thread;
use std::sync::{Arc, Mutex};

fn main() {
  let mut a : [u32; 10] = [0; 10];
  let mut b = Arc::new(Mutex::new([0; 10]));
  let mut rng = rand::thread_rng();

  for x in 0..9 {
    a[x] = (rng.gen::<u32>()  % 1000) + 1;
  };

  for x in 0..4 { 
    let b = b.clone(); 
    thread::spawn(move || { let mut b = b.lock().unwrap();
      for y in 0..4 {
        b[x]   += a[y] * a[y*2+1];
        b[x+5] += a[y+1] * a[y*2];
      }
    });
  };

  thread::sleep_ms(1000); 

  for x in 0..a.len() {
    println!("a({0})={1}, b({0})={2}", x, a[x], b[x]);
  };
}

Can you help me:

  1. if I use: let mut b = Arc::new(Mutex::new([u32; 10] = [0; 10])); -> I get error unresolved name 'u32'. Did you mean 'a'? How can I set the type of array element ?
  2. thread::sleep_ms(1000) - It is so rudely. How can I check that all thread is finished?
  3. How can I get back my calculated b[i] and/or gather thread-calculated b-arrays in the final one ? Now I've got error: cannot index a value of type 'alloc::arc::Arc<std::sync::mutex::Mutex<[u32; 10]>>'
  4. Can I use only one b-array in memory and send into thread (using pointers) to calculating two elements of b-array?

Thank for solutions. Working code is (I've modified it for show problem):

extern crate rand;
use rand::Rng;
use std::thread;
use std::sync::{Arc, Mutex};

fn main() {
  let mut a : [u32; 10000] = [0; 10000];
  let b = Arc::new(Mutex::new([0u32; 10]));
  let mut rng = rand::thread_rng();

  for x in 0..10000 {
    a[x] = (rng.gen::<u32>() % 10) + 1;
  };

  for x in 0..5 {
    let b = b.clone();
    thread::spawn(move || { let mut b = b.lock().unwrap();
      println!("thread {} started", x);
      for y in 0..5000 {
        b[x]   += a[y] * a[y*2+1];
        b[x+5] += a[y+1] * a[y*2];
      };
      b[x] += a[x];
      b[x+5] -= a[x];
    println!("thread {} finished", x);
    });
  };

  thread::sleep_ms(1000);

  for x in 0..10 {
    println!("b({0})={1}", x, b.lock().unwrap()[x]);
  };
}

The output is:

thread 1 started
thread 1 finished
thread 3 started
thread 3 finished
thread 0 started
thread 0 finished
thread 2 started
thread 2 finished
thread 4 started
thread 4 finished
b(0)=149482
...
b(9)=149065

Threads are processed step-by-step.

Joysi
  • 97
  • 2
  • 7
  • to question 4: Isn't that what you are already doing? every thread will calculate the value for `b[x]` and `b[x+5]` – oli_obk Jul 24 '15 at 10:42
  • 1
    note that on stackoverflow you should only be asking one question per question. – oli_obk Jul 24 '15 at 10:46
  • Thank you. I use five threads. Each thread clone b-array `let b = b.clone()` (we can have 5 copies b-array in memory), lock it and calculate b[x], b[x+5]. Can I clone and lock only the calculating Items (2 elements of b-array) without copy entirely? Can I don't use clone (lock calculating element in b-array)? Sorry for 4 questions, but it chained to each other. – Joysi Jul 24 '15 at 10:55
  • note that you are not cloning the array, but the `Arc`, which is just a thread-safe reference counted pointer. All the arcs you create point to the same array – oli_obk Jul 24 '15 at 12:53
  • 1
    Have you tried using a `Vec` instead of an array? allocating large objects on the stack will often get you into trouble. – oli_obk Jul 24 '15 at 12:57
  • I thought that reference to array store in the stack instead of entire array. – Joysi Jul 24 '15 at 13:10

1 Answers1

4

Note that the clone() method on the Arc object does not "clone" the array, simply it increments the reference counter of the Arc.

I think you are asking for a general strategy to process data in parallel in Rust. Your code lock the b array in each thread, so you have no parallel processing.

To do real parallel processing you would need a mutable access to the array without a lock on the entire array but you cannot do that in safe Rust.

To do that you have to use some sort of unsafe mechanism, such raw pointers.

This is a simple example to process a (non mutable) input vector into a (mutable) output vector concurrently:

use std::thread;
use std::sync::Arc;

fn main() {
    let input = Arc::new([1u32, 2, 3, 4]);
    let output = Arc::new([0; 4]);

    let mut handles = Vec::new();

    for t in 0..4 {
        let inp = input.clone();
        let out = output.clone();
        let handle = thread::spawn(move || unsafe {
            let p = (out.as_ptr() as *mut u32).offset(t as isize);

            *p = inp[t] + (t as u32 + 1);
        });

        handles.push(handle);
    }


    for h in handles {
        h.join().unwrap();
    }

    println!("{:?}", output);
}

You still need to use Arc to pass data into the threads and to have a proper lifetime management. Then inside the thread you need to get a mutable pointer to the data (out.as_ptr() as *mut u32), then the item processed in that thread using the offset method.

eulerdisk
  • 4,299
  • 1
  • 22
  • 21
  • Thank a lot! I've clarified some details for me. I can't have in safe mode a mutable access to the one array element without a lock entire array? Lock the one element step-by-step without locking the others ... – Joysi Jul 24 '15 at 12:08
  • It is not necessary to drop to unsafe code to solve this problem. A Mutex is one possible solution, splitting the slice is another. – Shepmaster Jul 24 '15 at 12:15
  • 2
    @Shepmaster A Mutex on the array would lock the entire array, so no parallel writing. A Mutex for each element or splitting the array are not good options for performance reasons. To clarify, my target use case is mainly numeric code, when for example you have a large array of matrices and you have to do operations on them. – eulerdisk Jul 24 '15 at 12:53
  • @AndreaP good points. I really miss thread::scoped. – Shepmaster Jul 24 '15 at 16:07
  • @eulerdisk - What if the case was to read and write for that array index parallely. Say a queue of array, where at each index queue where data can be added and parallelly popped? The would correct structure be Arc Mutex of array of Arc Mutex of Queue? – rohitpal May 27 '20 at 03:51