polevoy_sergey_lab_5 #215
11
polevoy_sergey_lab_5/cargo.toml
Normal file
11
polevoy_sergey_lab_5/cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "matrix"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
opt-level = 3
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[dependencies]
|
BIN
polevoy_sergey_lab_5/image.png
Normal file
BIN
polevoy_sergey_lab_5/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
12
polevoy_sergey_lab_5/readme.md
Normal file
12
polevoy_sergey_lab_5/readme.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Лабораторная работа №5
|
||||||
|
## Полевой Сергей ПИбд-42
|
||||||
|
### Реализация
|
||||||
|
Для выполнения работы было решено разбивать матрицы на диапазоны строк и столбцов, которые будут передаваться в потоки, которые в свою очередь результаты для этих диапазонов будут возвращать обратно в виде сообщений в канале mpsc для обеспечения безопасности доступа к памяти
|
||||||
|
|
||||||
|
### Скриншот работы
|
||||||
|
![alt text](image.png)
|
||||||
|
|
||||||
|
### Выводы
|
||||||
|
Исходя из полученых результатов можно сделать следующие выводы: использование потоков рационально только в случае больших матриц, так как для малых больше времени уходит на создание и управление потоками
|
||||||
|
|
||||||
|
### Демонстрация работы доступна по [ссылке](https://disk.yandex.ru/i/hRymVDcqrKDsoQ)
|
100
polevoy_sergey_lab_5/src/main.rs
Normal file
100
polevoy_sergey_lab_5/src/main.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use std::thread;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::time;
|
||||||
|
|
||||||
|
// Квадратная матрица хранится в куче как последовательность всех её элементов, при этом на этапе компиляции всегда известен её размер
|
||||||
|
struct Matrix<const N: usize> {
|
||||||
|
inner: Vec<[i32; N]>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Matrix<N> {
|
||||||
|
fn new() -> Matrix<N> {
|
||||||
|
Matrix { inner: vec![[0; N]; N] }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Псевдослучайная генерация данных для матрицы
|
||||||
|
fn random(seed: i32) -> Matrix<N> {
|
||||||
|
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<const N: usize, const M: usize>(f: &Matrix<N>, s: &Matrix<N>) -> Matrix<N> {
|
||||||
|
let mut result = Matrix::<N>::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<const N: usize, const M: usize>() {
|
||||||
|
let first = Matrix::<N>::random(1);
|
||||||
|
let second = Matrix::<N>::random(2);
|
||||||
|
|
||||||
|
let start_time = time::Instant::now();
|
||||||
|
multiply::<N, M>(&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>();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user