polevoy_sergey_lab_6 #218

Open
ChipsEater wants to merge 1 commits from polevoy_sergey_lab_6 into main
4 changed files with 149 additions and 0 deletions

View File

@ -0,0 +1,11 @@
[package]
name = "matrix"
version = "0.1.0"
edition = "2021"
[profile.release]
strip = true
opt-level = 3
panic = "abort"
[dependencies]

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@ -0,0 +1,13 @@
# Лабораторная работа №6
## Полевой Сергей ПИбд-42
### Реализация
Было решено распределять работу по потокам в виде диапазонов столбцов матрицы, по которым каждый поток по отдельности найдёт определитель минора, далее результаты работ каждого потока передаются по mpsc каналу и суммируется для окончательного результата.
#### Скриншот результата работы
![alt text](image.png)
#### Выводы
Подобно результату прошлой лабораторной работы, очевидного выигрыша программа достигает при должно большом размере матрицы, так как при малых значениях размера больше времени тратится на создание и управление потоками.
#### Демонстрация работы доступна по [ссылке](https://disk.yandex.ru/i/lv-Szz2Xzpvx7w)

View File

@ -0,0 +1,125 @@
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>();
}