# Лабораторная работа №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)