126 lines
4.1 KiB
Rust
126 lines
4.1 KiB
Rust
use std::sync;
|
||
use std::thread;
|
||
use std::time;
|
||
|
||
// Квадратная матрица хранится в куче как последовательность всех её элементов, при этом на этапе компиляции всегда известен её размер
|
||
struct Matrix<const N: usize> {
|
||
inner: Vec<[i16; N]>,
|
||
}
|
||
|
||
impl<const N: usize> Matrix<N> {
|
||
fn new() -> Matrix<N> {
|
||
Matrix {
|
||
inner: vec![[0; N]; N],
|
||
}
|
||
}
|
||
|
||
// Псевдослучайная генерация данных для матрицы
|
||
fn random(seed: i16) -> Matrix<N> {
|
||
let mut matrix = Matrix::new();
|
||
|
||
for i in 0..N {
|
||
for j in 0..N {
|
||
matrix.inner[i][j] = i as i16 + j as i16 - seed;
|
||
}
|
||
}
|
||
|
||
matrix
|
||
}
|
||
}
|
||
|
||
// Рекурсивное получение определителя по первой строке, также включает необязательные аргументы, необходимые для случая с параллельностью
|
||
fn recursive(matrix: &[&[i16]], from: Option<usize>, to: Option<usize>) -> i16 {
|
||
let size = matrix.len();
|
||
|
||
match size {
|
||
0 => unreachable!(),
|
||
1 => matrix[0][0],
|
||
2 => matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0],
|
||
3.. => (from.unwrap_or(0)..to.unwrap_or(size))
|
||
.map(|c| {
|
||
((-1i16).pow(c as u32))
|
||
* matrix[0][c]
|
||
* recursive(
|
||
&matrix
|
||
.iter()
|
||
.skip(1)
|
||
.map(|row| {
|
||
row.iter()
|
||
.zip(0..)
|
||
.filter(|(_, i)| *i != c)
|
||
.map(|(n, _)| *n)
|
||
.collect::<Vec<i16>>()
|
||
})
|
||
.collect::<Vec<Vec<i16>>>()
|
||
.iter()
|
||
.map(|r| r.as_slice())
|
||
.collect::<Vec<&[i16]>>(),
|
||
None,
|
||
None,
|
||
)
|
||
})
|
||
.sum(),
|
||
}
|
||
}
|
||
|
||
fn parallel<const N: usize, const M: usize>(matrix: &Matrix<N>) -> i16 {
|
||
let (result_sender, result_receiver) = sync::mpsc::channel::<i16>();
|
||
let matrix: &[&[i16]] = &matrix
|
||
.inner
|
||
.iter()
|
||
.map(|r| r.as_slice())
|
||
.collect::<Vec<&[i16]>>();
|
||
|
||
thread::scope(move |scope| {
|
||
let chunk_size = N / M;
|
||
let remainder = N % M;
|
||
|
||
let mut chunks = Vec::new();
|
||
|
||
let mut start_col = 0;
|
||
for i in 0..M {
|
||
let end_col = start_col + chunk_size + if i < remainder { 1 } else { 0 };
|
||
chunks.push((start_col, end_col));
|
||
start_col = end_col;
|
||
}
|
||
|
||
chunks.into_iter().for_each(|(start_col, end_col)| {
|
||
let sender = result_sender.clone();
|
||
scope.spawn(move || sender.send(recursive(matrix, Some(start_col), Some(end_col))).unwrap());
|
||
});
|
||
});
|
||
|
||
result_receiver.into_iter().sum()
|
||
}
|
||
|
||
// Замер времени выполнения, при этом размеры матриц и количество потоков известны на этапе компиляции
|
||
fn benchmark<const N: usize, const M: usize>() {
|
||
let matrix = Matrix::<N>::random(0);
|
||
|
||
let start_time = time::Instant::now();
|
||
parallel::<N, M>(&matrix);
|
||
println!("Определитель для матрицы {0:>4}x{0:<4}: {1} сек ({2} потоков)", N, (time::Instant::now() - start_time).as_secs_f64(), M)
|
||
}
|
||
|
||
fn main() {
|
||
benchmark::<8, 1>();
|
||
benchmark::<8, 2>();
|
||
benchmark::<8, 4>();
|
||
benchmark::<8, 8>();
|
||
|
||
benchmark::<10, 1>();
|
||
benchmark::<10, 2>();
|
||
benchmark::<10, 4>();
|
||
benchmark::<10, 8>();
|
||
|
||
benchmark::<11, 1>();
|
||
benchmark::<11, 2>();
|
||
benchmark::<11, 4>();
|
||
benchmark::<11, 8>();
|
||
|
||
benchmark::<12, 1>();
|
||
benchmark::<12, 2>();
|
||
benchmark::<12, 4>();
|
||
benchmark::<12, 8>();
|
||
}
|