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>();
|
|||
|
}
|