2

I created a two methods one synchronous and one with multiple threads because I wanted to compare performance synchronous and parallel method. But I am having a one issue every time when I want to use my data in threads I have to copy them first even if I know that they want to be dropped till the end of this method. If I do not copy this data before using in threads then I am getting an error that I have to make my data 'static:

fn parallel_kronecker_product(&self, matrix: &Matrix) -> Matrix {
    let product_rows = self.rows_count * matrix.rows_count;
    let product_columns_count = self.columns_count * matrix.columns_count;
    let product = Arc::new(Mutex::new(Matrix::new_zeros_matrix(
        product_rows,
        product_columns_count,
    )));
    let mut handles = vec![];
    for m1_row_index in 0..self.rows_count {
        let product = Arc::clone(&product);
        let matrix_a = self.to_owned();
        let matrix_b = matrix.to_owned();
        handles.push(
            thread::spawn(move || {
                for m1_column_index in 0..matrix_a.columns_count {
                    for m2_row_index in 0..matrix_b.rows_count {
                        for m2_column_index in 0..matrix_b.columns_count {
                            let product_row_index = m1_row_index * matrix_b.rows_count + m2_row_index;
                            let product_column_index =
                                m1_column_index * matrix_b.columns_count + m2_column_index;
                            let mut prod = product.lock().unwrap();
                            (*prod)[product_row_index][product_column_index] = matrix_a[m1_row_index]
                                [m1_column_index]
                                * matrix_b[m2_row_index][m2_column_index];
                        }
                    }
                }
            })
        );
    }
    for handle in handles {
        handle.join().unwrap();
    }
    return product.lock().unwrap().clone();
}

So here I have two matrices. Base which is immutable self and one from parameter matrix. Inside for m2_row_index in 0..matrix_b.rows_count loop I am only multiplying some data which doesn't change original data. Then I iterate over all threads to tell rust to wait until all threads finish their job, so nothing outside this method scope should drop this matrix

Can you tell me, what can I do to do not copy this data?

Krzysztof Kaczyński
  • 4,412
  • 6
  • 28
  • 51
  • Peter's answer shows how to apply the answer to the linked question to your case. If this isn't what you're looking for, please [edit] the question to explain how it's different. Thanks! – trent Feb 22 '21 at 22:35

1 Answers1

2

You can use a scoped thread from a third party crate. There are a few to choose from, but a popular one is from crossbeam. The reason this is needed is because the types used for threads spawned with std::thread::spawn do not carry information about how long they last, even if you are explicitly joining them. Crossbeam's scoped threads are bound to the lifetime of the surrounding Scope so the borrow checker can be sure that they are finished with borrowed data when the scope ends.

Your provided code has a lot of definitions missing, so I didn't try to compile it, but the general idea would be this:

fn parallel_kronecker_product(&self, matrix: &Matrix) -> Matrix {

    // Create a new thread scope and make it own the locals
    thread::scope(move |scope| {
        let product_rows = self.rows_count * matrix.rows_count;
        let product_columns_count = self.columns_count * matrix.columns_count;
        let product = Arc::new(Mutex::new(Matrix::new_zeros_matrix(
            product_rows,
            product_columns_count,
        )));

        let mut handles = vec![];
        for m1_row_index in 0..self.rows_count {
            let product = Arc::clone(&product);
            let matrix_a = self.to_owned();
            let matrix_b = matrix.to_owned();

            // spawn a new thread inside the scope which owns the data it needs to borrow
            handles.push(scope.spawn(move |_| {
                for m1_column_index in 0..matrix_a.columns_count {
                    for m2_row_index in 0..matrix_b.rows_count {
                        for m2_column_index in 0..matrix_b.columns_count {
                            let product_row_index =
                                m1_row_index * matrix_b.rows_count + m2_row_index;
                            let product_column_index =
                                m1_column_index * matrix_b.columns_count + m2_column_index;
                            let mut prod = product.lock().unwrap();
                            (*prod).ind();
                        }
                    }
                }
            }));
        }
        for handle in handles {
            handle.join().unwrap();
        }

        // probably need a new local binding here. For... reasons...
        let product = product.lock().unwrap().clone();
        product
    }).unwrap()
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204