# Лабораторная работа №5 - Параллельное умножение матриц

## Задание
В данной лабораторной работе было необходимо реализовать умножение двух квадратных матриц двумя способами:
1. Последовательное умножение.
2. Параллельное умножение с возможностью задания количества потоков.

Основная цель — изучить эффект параллельных вычислений и оценить прирост производительности при увеличении количества потоков.

## Запуск
Для запуска алгоритма умножения матриц потребуется Python и модуль `multiprocessing`.

Запуск теста производительности:
```
    python matrix_multiplication_benchmark.py
```

## Результаты

Ниже представлены результаты бенчмарков для различных размеров матриц и разного количества потоков:

| Размер матрицы | Потоки | Время (последовательное) | Время (параллельное) | Ускорение |
|----------------|--------|--------------------------|-----------------------|-----------|
| **100x100**    | 1      | 0.0000 s                | 0.2030 s             | 0.00      |
|                | 2      | 0.0000 s                | 0.1770 s             | 0.00      |
|                | 4      | 0.0000 s                | 0.1976 s             | 0.00      |
|                | 6      | 0.0000 s                | 0.3170 s             | 0.00      |
|                | 8      | 0.0000 s                | 0.2330 s             | 0.00      |
|                | 12     | 0.0000 s                | 0.2570 s             | 0.00      |
|                | 16     | 0.0000 s                | 0.2705 s             | 0.00      |

| Размер матрицы | Потоки | Время (последовательное) | Время (параллельное) | Ускорение |
|----------------|--------|--------------------------|-----------------------|-----------|
| **500x500**    | 1      | 0.0621 s                | 0.2400 s             | 0.26      |
|                | 2      | 0.0621 s                | 0.2270 s             | 0.27      |
|                | 4      | 0.0621 s                | 0.2320 s             | 0.27      |
|                | 6      | 0.0621 s                | 0.2744 s             | 0.23      |
|                | 8      | 0.0621 s                | 0.3260 s             | 0.19      |
|                | 12     | 0.0621 s                | 0.4322 s             | 0.14      |
|                | 16     | 0.0621 s                | 0.5961 s             | 0.10      |

| Размер матрицы | Потоки | Время (последовательное) | Время (параллельное) | Ускорение |
|----------------|--------|--------------------------|-----------------------|-----------|
| **1000x1000**  | 1      | 0.6100 s                | 0.8320 s             | 0.73      |
|                | 2      | 0.6100 s                | 0.5252 s             | 1.16      |
|                | 4      | 0.6100 s                | 0.4431 s             | 1.38      |
|                | 6      | 0.6100 s                | 0.4750 s             | 1.28      |
|                | 8      | 0.6100 s                | 0.5668 s             | 1.08      |
|                | 12     | 0.6100 s                | 0.7913 s             | 0.77      |
|                | 16     | 0.6100 s                | 0.9752 s             | 0.63      |

| Размер матрицы | Потоки | Время (последовательное) | Время (параллельное) | Ускорение |
|----------------|--------|--------------------------|-----------------------|-----------|
| **2500x2500**  | 1      | 15.6168 s               | 14.9276 s            | 1.05      |
|                | 2      | 15.6168 s               | 10.0759 s            | 1.55      |
|                | 4      | 15.6168 s               | 6.5090 s             | 2.40      |
|                | 6      | 15.6168 s               | 6.1926 s             | 2.52      |
|                | 8      | 15.6168 s               | 5.7073 s             | 2.74      |
|                | 12     | 15.6168 s               | 5.7048 s             | 2.74      |
|                | 16     | 15.6168 s               | 6.1797 s             | 2.53      |

| Размер матрицы | Потоки | Время (последовательное) | Время (параллельное) | Ускорение |
|----------------|--------|--------------------------|-----------------------|-----------|
| **4000x4000**  | 1      | 153.4893 s              | 142.6276 s           | 1.08      |
|                | 2      | 153.4893 s              | 72.1959 s            | 2.13      |
|                | 4      | 153.4893 s              | 41.6678 s            | 3.68      |
|                | 6      | 153.4893 s              | 33.3039 s            | 4.61      |
|                | 8      | 153.4893 s              | 30.3821 s            | 5.05      |
|                | 12     | 153.4893 s              | 27.2013 s            | 5.64      |
|                | 16     | 153.4893 s              | 29.0246 s            | 5.29      |

| Количество потоков | Загрузка процессора (%) |
|--------------------|-------------------------|
| 1 поток            | 12%                     |
| 2 потока           | 22-24%                  |
| 4 потока           | 44-46%                  |
| 6 потоков          | 64-67%                  |
| 8 потоков          | 85-87%                  |
| 12 потоков         | 92-96%                  |
| 16 потоков         | 95-96%                  |
## Выводы
Из результатов видно, что ускорение при использовании параллельного алгоритма зависит от размера матрицы и количества потоков:
- Для небольших матриц (например, 100x100) параллельный алгоритм не дает значительного ускорения и даже может работать медленнее из-за накладных расходов на создание потоков.
- Для больших матриц (например, 4000x4000) параллельный алгоритм дает существенное ускорение, достигая оптимальных значений на 8–12 потоках.
- Если количество потоков превышает количество ядер процессора, наблюдается ухудшение производительности, так как накладные расходы на управление потоками начинают превышать прирост от параллелизма.
- Загруженность процессора: При увеличении количества потоков наблюдается значительное повышение загрузки процессора. На конфигурации с 16 потоками процессор работает на 95-96%, что указывает на высокую эффективность использования доступных ядер. Однако увеличение числа потоков сверх количества доступных ядер (например, использование 16 потоков на 8-ядерном процессоре) не дает заметного увеличения производительности и может даже привести к ее снижению из-за конкуренции за ресурсы.
- Таким образом, для эффективного параллельного умножения матриц важно учитывать размер данных и правильно подбирать количество потоков.

## [Видео](https://disk.yandex.ru/i/aYvYD_fHQLfdZQ)