Merge pull request 'minhasapov_ruslan_lab_5' (#210) from minhasapov_ruslan_lab_5 into main
Reviewed-on: #210
This commit is contained in:
commit
ce580c4572
20
minhasapov_ruslan_lab_5/.gitignore
vendored
Normal file
20
minhasapov_ruslan_lab_5/.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
*.fsproj.user
|
||||
*.fsproj.cache
|
||||
*.fsproj
|
||||
*.suo
|
||||
*.user
|
||||
*.cache
|
||||
bin/
|
||||
obj/
|
||||
|
||||
*.dll
|
||||
*.exe
|
||||
*.pdb
|
||||
*.zip
|
||||
*.tar.gz
|
||||
|
||||
*~
|
||||
*.bak
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
77
minhasapov_ruslan_lab_5/MatrixMultiplication/Program.fs
Normal file
77
minhasapov_ruslan_lab_5/MatrixMultiplication/Program.fs
Normal file
@ -0,0 +1,77 @@
|
||||
open System
|
||||
open System.Threading
|
||||
open System.Diagnostics
|
||||
|
||||
type Matrix = double[,]
|
||||
|
||||
// Функция для умножения части матрицы
|
||||
let multiplyPart (A: Matrix) (B: Matrix) (C: Matrix) startRow endRow =
|
||||
let size = A.GetLength(0)
|
||||
for i in startRow .. endRow - 1 do
|
||||
for j in 0 .. size - 1 do
|
||||
for k in 0 .. size - 1 do
|
||||
C.[i, j] <- C.[i, j] + A.[i, k] * B.[k, j]
|
||||
|
||||
// Последовательное умножение матриц
|
||||
let multiplySequential (A: Matrix) (B: Matrix) : Matrix =
|
||||
let size = A.GetLength(0)
|
||||
let C = Array2D.zeroCreate size size
|
||||
multiplyPart A B C 0 size
|
||||
C
|
||||
|
||||
// Параллельное умножение матриц с синхронизацией
|
||||
let multiplyParallel (A: Matrix) (B: Matrix) numThreads : Matrix =
|
||||
let size = A.GetLength(0)
|
||||
let C = Array2D.zeroCreate size size
|
||||
let rowsPerThread = size / numThreads
|
||||
let threads = Array.zeroCreate numThreads
|
||||
|
||||
for i in 0 .. numThreads - 1 do
|
||||
let startRow = i * rowsPerThread
|
||||
let endRow = if i = numThreads - 1 then size else startRow + rowsPerThread
|
||||
threads.[i] <- new Thread(fun () -> multiplyPart A B C startRow endRow)
|
||||
|
||||
// Запускаем потоки
|
||||
threads |> Array.iter (fun t -> t.Start())
|
||||
|
||||
// Ожидаем завершения всех потоков
|
||||
threads |> Array.iter (fun t -> t.Join())
|
||||
|
||||
C
|
||||
|
||||
// Функция для генерации матрицы
|
||||
let generateMatrix size =
|
||||
let matrix = Array2D.zeroCreate size size
|
||||
let rnd = Random()
|
||||
for i in 0 .. size - 1 do
|
||||
for j in 0 .. size - 1 do
|
||||
matrix.[i, j] <- double (rnd.Next(10))
|
||||
matrix
|
||||
|
||||
[<EntryPoint>]
|
||||
let main argv =
|
||||
let sizes = [ 100; 300; 500 ]
|
||||
let threadCounts = [ 1; 2; 4; 8; 16; 32; 64; 128; 256; 512]
|
||||
|
||||
// Заголовки таблицы
|
||||
printfn "| Размер | Потоков | Алгоритм | Время (мс) |"
|
||||
|
||||
for size in sizes do
|
||||
for numThreads in threadCounts do
|
||||
let A = generateMatrix size
|
||||
let B = generateMatrix size
|
||||
|
||||
let stopwatch = Stopwatch.StartNew()
|
||||
|
||||
let algorithm =
|
||||
if numThreads = 1 then
|
||||
multiplySequential A B |> ignore; "Последовательный"
|
||||
else
|
||||
multiplyParallel A B numThreads |> ignore; "Параллельный"
|
||||
|
||||
stopwatch.Stop()
|
||||
|
||||
// Вывод результатов в виде таблицы
|
||||
printfn "| %dx%d | %d | %s | %d |" size size numThreads algorithm stopwatch.ElapsedMilliseconds
|
||||
|
||||
0
|
87
minhasapov_ruslan_lab_5/README.md
Normal file
87
minhasapov_ruslan_lab_5/README.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Лабораторная работа №5
|
||||
#### ПИбд-42. Минхасапов Руслан.
|
||||
|
||||
---
|
||||
|
||||
**Цель:** Реализовать последовательный и параллельный алгоритмы умножения квадратных матриц и сравнить их производительность.
|
||||
|
||||
---
|
||||
|
||||
**Описание задачи:** В работе реализованы два алгоритма умножения матриц:
|
||||
|
||||
* **Последовательный:** Классический алгоритм умножения матриц с тремя вложенными циклами.
|
||||
* **Параллельный:** Алгоритм, распараллеливающий вычисления по строкам результирующей матрицы. Каждому потоку назначается подмножество строк для вычисления. Число потоков задается параметром.
|
||||
|
||||
---
|
||||
|
||||
**Описание параллельного алгоритма:**
|
||||
|
||||
Параллельный алгоритм `multiplyParallel` разделяет вычисления между заданным количеством потоков. Входные матрицы `A` и `B`, а также результирующая матрица `C` передаются в каждый поток.
|
||||
|
||||
1. **Разделение по строкам:** Матрица `C` разделяется на блоки по строкам. Каждый поток отвечает за вычисление элементов в своем блоке строк. Количество строк на поток рассчитывается как `size / numThreads`, где `size` - размер матрицы, а `numThreads` - количество потоков.
|
||||
|
||||
2. **Создание потоков:** Создается массив потоков `threads`. Каждый поток выполняет функцию `multiplyPart`, которая вычисляет значения элементов в заданном диапазоне строк.
|
||||
|
||||
3. **Запуск и синхронизация:** Все потоки запускаются с помощью `t.Start()`. Затем основной поток ожидает завершения всех потоков с помощью `t.Join()`. Это гарантирует, что все вычисления будут завершены до возвращения результата.
|
||||
|
||||
4. **Возврат результата:** После завершения всех потоков функция `multiplyParallel` возвращает результирующую матрицу `C`.
|
||||
|
||||
---
|
||||
|
||||
**Результаты бенчмарков:**
|
||||
|
||||
Были проведены тесты для матриц размером 100x100, 300x300 и 500x500 элементов с разным количеством процессов (2, 4, 8, 16, 32, 64, 128, 256, 512). Результаты представлены в таблице:
|
||||
|
||||
| Размер | Кол-во потоков | Алгоритм | Время (мс) |
|
||||
|---|---|---|---|
|
||||
| 100x100 | 1 | Последовательный | 11 |
|
||||
| 100x100 | 2 | Параллельный | 7 |
|
||||
| 100x100 | 4 | Параллельный | 6 |
|
||||
| 100x100 | 8 | Параллельный | 5 |
|
||||
| 100x100 | 16 | Параллельный | 6 |
|
||||
| 100x100 | 32 | Параллельный | 6 |
|
||||
| 100x100 | 64 | Параллельный | 14 |
|
||||
| 100x100 | 128 | Параллельный | 27 |
|
||||
| 100x100 | 256 | Параллельный | 44 |
|
||||
| 100x100 | 512 | Параллельный | 69 |
|
||||
| 300x300 | 1 | Последовательный | 393 |
|
||||
| 300x300 | 2 | Параллельный | 177 |
|
||||
| 300x300 | 4 | Параллельный | 197 |
|
||||
| 300x300 | 8 | Параллельный | 155 |
|
||||
| 300x300 | 16 | Параллельный | 148 |
|
||||
| 300x300 | 32 | Параллельный | 139 |
|
||||
| 300x300 | 64 | Параллельный | 153 |
|
||||
| 300x300 | 128 | Параллельный | 154 |
|
||||
| 300x300 | 256 | Параллельный | 167 |
|
||||
| 300x300 | 512 | Параллельный | 358 |
|
||||
| 500x500 | 1 | Последовательный | 1616 |
|
||||
| 500x500 | 2 | Параллельный | 831 |
|
||||
| 500x500 | 4 | Параллельный | 598 |
|
||||
| 500x500 | 8 | Параллельный | 522 |
|
||||
| 500x500 | 16 | Параллельный | 542 |
|
||||
| 500x500 | 32 | Параллельный | 578 |
|
||||
| 500x500 | 64 | Параллельный | 642 |
|
||||
| 500x500 | 128 | Параллельный | 812 |
|
||||
| 500x500 | 256 | Параллельный | 1038 |
|
||||
| 500x500 | 512 | Параллельный | 1496 |
|
||||
|
||||
---
|
||||
|
||||
**Анализ результатов:**
|
||||
|
||||
* **Влияние размера матрицы:** Время выполнения обоих алгоритмов ожидаемо растет с увеличением размера матрицы, подтверждая кубическую сложность O(n³).
|
||||
|
||||
* **Влияние числа потоков:** Параллельный алгоритм демонстрирует ускорение по сравнению с последовательным. Наиболее заметное ускорение наблюдается при умеренном количестве потоков. Для матрицы 100x100 оптимальное количество потоков — 8, для 300x300 — 32, а для 500x500 — 8.
|
||||
|
||||
* **Ухудшение производительности при большом числе потоков:** При слишком большом количестве потоков (64, 128, 256, 512) производительность начинает ухудшаться из-за накладных расходов на создание, управление и синхронизацию потоков. Эти расходы перевешивают выгоду от распараллеливания, особенно на относительно небольших матрицах.
|
||||
|
||||
* **Оптимальное число потоков:** Оптимальное количество потоков нелинейно зависит от размера матрицы и, вероятно, ограничено количеством логических ядер процессора. В данном случае, для матриц большего размера оптимальное число потоков меньше максимального протестированного, что указывает на наличие узкого места, связанного с управлением потоками.
|
||||
|
||||
**Выводы:**
|
||||
|
||||
Параллельное умножение матриц может значительно ускорить вычисления, но требует настройки количества потоков. Слишком большое количество потоков приводит к снижению производительности. Оптимальное количество потоков близко к количеству логических ядер процессора (8), но может варьироваться в зависимости от размера матрицы и других факторов. Результаты подчеркивают важность баланса между распараллеливанием вычислений и накладными расходами на управление потоками.
|
||||
|
||||
---
|
||||
|
||||
#### Демонстрация
|
||||
Видео доступно по [ссылке](https://disk.yandex.ru/i/aG-7_gTgRwqf5A)
|
Loading…
Reference in New Issue
Block a user