diff --git a/nikolaeva_yana_lab_5/README.md b/nikolaeva_yana_lab_5/README.md new file mode 100644 index 0000000..92b0243 --- /dev/null +++ b/nikolaeva_yana_lab_5/README.md @@ -0,0 +1,62 @@ +# Лабораторная работа: Умножение матриц + +## Описание + +**Цель работы** – реализовать последовательный и параллельный алгоритмы умножения матриц, а также сравнить их производительность на больших квадратных матрицах. + +### Задачи: +1. Реализовать последовательный алгоритм умножения матриц. +2. Реализовать параллельный алгоритм, позволяющий задавать количество потоков вручную. +3. Провести тесты на матрицах размером 100x100, 300x300 и 500x500. +4. Сделать выводы о влиянии размеров матриц и количества потоков на производительность алгоритмов. + +## Теоретическое обоснование + +Умножение матриц — вычислительно сложная операция с асимптотической сложностью O(N³) для матриц размером N×N. +Для ускорения вычислений используется параллелизация, где каждая часть вычислений выполняется в отдельном потоке. +Однако эффективность параллельного подхода зависит от размеров задачи и числа потоков. + +## Реализация + +1. **Последовательный алгоритм**: + - Выполняет вычисления поэлементно для каждой строки первой матрицы и каждого столбца второй. + - Этот алгоритм не использует дополнительные ресурсы, кроме одного потока, и подходит для небольших задач. + +2. **Параллельный алгоритм**: + - Делит строки первой матрицы на группы, каждая из которых обрабатывается в отдельном потоке. + - Реализован с использованием модуля `multiprocessing` для управления потоками. + - Число потоков задается вручную для возможности анализа производительности. + +## Результаты тестирования + +### Условия тестирования +- Размеры матриц: 100x100, 300x300, 500x500. +- Количество потоков: 1 (последовательное выполнение), 2, 4. +- Диапазон значений элементов матриц: от 0 до 200. + + +## Выводы + +1. **Последовательный алгоритм**: + - Подходит для матриц небольшого размера (100x100), где накладные расходы на параллелизацию превышают выигрыши от многопоточности. + +2. **Параллельный алгоритм**: + - Значительно ускоряет умножение матриц с увеличением их размера. + - Для матриц 500x500 ускорение в 2–2.5 раза при переходе от 1 потока к 4 потокам. + +3. **Влияние числа потоков**: + - Оптимальное число потоков зависит от размера задачи и доступных ресурсов. + - Слишком большое количество потоков может привести к росту накладных расходов. + +4. **Закономерности**: + - Накладные расходы на управление потоками минимальны для больших задач. + - Параллельные алгоритмы демонстрируют преимущество на задачах с высокой вычислительной сложностью. + +## Заключение + +Лабораторная работа подтвердила, что параллельные алгоритмы значительно эффективнее на больших данных. +Однако для небольших задач последовательный алгоритм остается предпочтительным из-за отсутствия накладных расходов. +В реальных приложениях важно учитывать баланс между размером задачи и доступными вычислительными ресурсами. + +## Видео +https://cloud.mail.ru/public/fykM/jy3KEZBZM diff --git a/nikolaeva_yana_lab_5/img.png b/nikolaeva_yana_lab_5/img.png new file mode 100644 index 0000000..40dc5d9 Binary files /dev/null and b/nikolaeva_yana_lab_5/img.png differ diff --git a/nikolaeva_yana_lab_5/main.py b/nikolaeva_yana_lab_5/main.py new file mode 100644 index 0000000..cc9658f --- /dev/null +++ b/nikolaeva_yana_lab_5/main.py @@ -0,0 +1,66 @@ +import numpy as np +import time +from multiprocessing import Pool, cpu_count + + +def generate_matrix(size, value_range=(0, 200)): + return np.random.randint(value_range[0], value_range[1], (size, size)) + + +def multiply_matrices_sequential(matrix_a, matrix_b): + size = len(matrix_a) + result = np.zeros((size, size), dtype=int) + for i in range(size): + for j in range(size): + for k in range(size): + result[i][j] += matrix_a[i][k] * matrix_b[k][j] + return result + + +def worker_multiply(args): + i_range, matrix_a, matrix_b, size = args + result_part = np.zeros((len(i_range), size), dtype=int) + for idx, i in enumerate(i_range): + for j in range(size): + for k in range(size): + result_part[idx][j] += matrix_a[i][k] * matrix_b[k][j] + return result_part + + +def multiply_matrices_parallel(matrix_a, matrix_b, num_threads): + size = len(matrix_a) + step = size // num_threads + ranges = [range(i, min(i + step, size)) for i in range(0, size, step)] + + with Pool(processes=num_threads) as pool: + results = pool.map(worker_multiply, [(i_range, matrix_a, matrix_b, size) for i_range in ranges]) + + return np.vstack(results) + +def benchmark(matrix_a, matrix_b, num_threads): + print(f"\nМатрицы размера {len(matrix_a)}x{len(matrix_a)}:") + + start = time.time() + result_seq = multiply_matrices_sequential(matrix_a, matrix_b) + sequential_time = time.time() - start + print(f"Последовательное умножение заняло: {sequential_time:.4f} секунд") + + start = time.time() + result_par = multiply_matrices_parallel(matrix_a, matrix_b, num_threads) + parallel_time = time.time() - start + print(f"Параллельное умножение ({num_threads} потоков) заняло: {parallel_time:.4f} секунд") + + assert np.array_equal(result_seq, result_par), "Ошибка: результаты не совпадают!" + print("Результаты совпадают.") + + return sequential_time, parallel_time + + +if __name__ == "__main__": + sizes = [100, 300, 500] + num_threads = min(cpu_count(), 4) + + for size in sizes: + matrix_a = generate_matrix(size) + matrix_b = generate_matrix(size) + benchmark(matrix_a, matrix_b, num_threads)