diff --git a/polevoy_sergey_lab_5/cargo.toml b/polevoy_sergey_lab_5/cargo.toml new file mode 100644 index 0000000..42b884c --- /dev/null +++ b/polevoy_sergey_lab_5/cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "matrix" +version = "0.1.0" +edition = "2021" + +[profile.release] +strip = true +opt-level = 3 +panic = "abort" + +[dependencies] diff --git a/polevoy_sergey_lab_5/image.png b/polevoy_sergey_lab_5/image.png new file mode 100644 index 0000000..d92b5b3 Binary files /dev/null and b/polevoy_sergey_lab_5/image.png differ diff --git a/polevoy_sergey_lab_5/readme.md b/polevoy_sergey_lab_5/readme.md new file mode 100644 index 0000000..b39617f --- /dev/null +++ b/polevoy_sergey_lab_5/readme.md @@ -0,0 +1,12 @@ +# Лабораторная работа №5 +## Полевой Сергей ПИбд-42 +### Реализация +Для выполнения работы было решено разбивать матрицы на диапазоны строк и столбцов, которые будут передаваться в потоки, которые в свою очередь результаты для этих диапазонов будут возвращать обратно в виде сообщений в канале mpsc для обеспечения безопасности доступа к памяти + +### Скриншот работы +![alt text](image.png) + +### Выводы +Исходя из полученых результатов можно сделать следующие выводы: использование потоков рационально только в случае больших матриц, так как для малых больше времени уходит на создание и управление потоками + +### Демонстрация работы доступна по [ссылке](https://disk.yandex.ru/i/hRymVDcqrKDsoQ) \ No newline at end of file diff --git a/polevoy_sergey_lab_5/src/main.rs b/polevoy_sergey_lab_5/src/main.rs new file mode 100644 index 0000000..1beeb1e --- /dev/null +++ b/polevoy_sergey_lab_5/src/main.rs @@ -0,0 +1,100 @@ +use std::thread; +use std::sync::mpsc; +use std::time; + +// Квадратная матрица хранится в куче как последовательность всех её элементов, при этом на этапе компиляции всегда известен её размер +struct Matrix { + inner: Vec<[i32; N]> +} + +impl Matrix { + fn new() -> Matrix { + Matrix { inner: vec![[0; N]; N] } + } + + // Псевдослучайная генерация данных для матрицы + fn random(seed: i32) -> Matrix { + let mut matrix = Matrix::new(); + + for i in 0..N { + for j in 0..N { + matrix.inner[i][j] = i as i32 + j as i32 - seed; + } + } + + matrix + } +} + +// Перемножение матриц с помощью произвольного количества потоков, размер матриц и количество потоков известно на этапе компиляции +fn multiply(f: &Matrix, s: &Matrix) -> Matrix { + let mut result = Matrix::::new(); + let part_size = N / M; + let last_part_size = N - (N / M) * (M - 1); + let (sender, receiver) = mpsc::channel(); + + // Конструкция scope гарантирует, что созданные внутри потоки завершат работу там же и не буду существовать дольше, чем сами данные + thread::scope(move |scope| { + for n_part in 1..M { + let sender = sender.clone(); + scope.spawn(move || { + for row in (if n_part != 0 {n_part - 1} else {n_part}) * part_size..n_part * part_size { + let mut data = Box::new([0; N]); + for col in 0..N { + data[col] = f.inner[row].iter().zip(s.inner.iter().map(|row| row[col])).map(|(v1, v2)| v1 * v2).sum() + } + sender.send((row, data)).unwrap(); + } + }); + } + // В случае одного потока или если осталась часть матрицы, которую не удалось передать в поток + if last_part_size != 0 { + for row in N-last_part_size..N { + let mut data = Box::new([0; N]); + for col in 0..N { + data[col] = f.inner[row].iter().zip(s.inner.iter().map(|row| row[col])).map(|(v1, v2)| v1 * v2).sum() + } + sender.send((row, data)).unwrap(); + } + } + }); + + // Потоки отдают результаты в виде сообщений в mpsc канале, при этом передаётся указатель на данные, а не сами данные + while let Ok((row, data)) = receiver.recv() { + for col in 0..N { + result.inner[row][col] = data[col]; + } + } + + result +} + +// Замер времени выполнения, при этом размеры матриц и количество потоков известны на этапе компиляции +fn benchmark() { + let first = Matrix::::random(1); + let second = Matrix::::random(2); + + let start_time = time::Instant::now(); + multiply::(&first, &second); + println!("{0:>4}x{0:<4} ({1:>2} потоков): {2} сек", N, M, (time::Instant::now() - start_time).as_secs_f64()) +} + +fn main() { + benchmark::<100, 1>(); + benchmark::<300, 1>(); + benchmark::<500, 1>(); + benchmark::<1000, 1>(); + benchmark::<1200, 1>(); + + benchmark::<100, 4>(); + benchmark::<300, 4>(); + benchmark::<500, 4>(); + benchmark::<1000, 4>(); + benchmark::<1200, 4>(); + + benchmark::<100, 8>(); + benchmark::<300, 8>(); + benchmark::<500, 8>(); + benchmark::<1000, 8>(); + benchmark::<1200, 8>(); +}