diff --git a/pupkov_alexey_lab_5/README.md b/pupkov_alexey_lab_5/README.md new file mode 100644 index 0000000..8a4b1cd --- /dev/null +++ b/pupkov_alexey_lab_5/README.md @@ -0,0 +1,70 @@ +# Пупков Алексей ИСЭбд-41 +# Отчет по умножению матриц +## Описание + +В данной лабораторной работе реализованы два алгоритма для умножения больших квадратных матриц: последовательный и параллельный. Для параллельного вычисления используется библиотека concurrent.futures в Python. Программа позволяет задавать количество процессов, что позволяет наблюдать за изменением производительности при увеличении числа потоков. + +## Как работает код: + +1. Импорт библиотек +- concurrent.futures помогает запускать параллельные задачи с использованием потоков +- time используется для измерения времени выполнения алгоритмов +- numpy используется для работы с матрицами и для вычислений +- argparse для обработки аргументов командной строки + +2. Функция matrix_multiply_sequential(A, B) +- Выполняет последовательное умножение двух матриц A и B. + +3. Функция worker(A, B, C, start_row, end_row) +- Выполняет умножение для части строк матрицы, параметры start_row и end_row определяют диапазон строк, которые нужно вычислить + +4. Функция matrix_multiply_parallel(A, B, num_threads) +- Реализует параллельное умножение матриц + +5. Функция benchmark(matrix_sizes, num_threads_list) + +- Выполняет бенчмаркинг (измерение времени выполнения) для обоих методов умножения + +## Результаты + +Размер матриц: 100x100 +Последовательное умножение заняло: 0.0558 секунд + +Параллельное умножение с 2 потоками заняло: 0.0886 секунд + +Параллельное умножение с 4 потоками заняло: 0.0876 секунд + +Параллельное умножение с 8 потоками заняло: 0.0868 секунд + +Параллельное умножение с 16 потоками заняло: 0.0914 секунд + +Размер матриц: 300x300 +Последовательное умножение заняло: 1.6149 секунд + +Параллельное умножение с 2 потоками заняло: 2.4936 секунд + +Параллельное умножение с 4 потоками заняло: 2.4383 секунд + +Параллельное умножение с 8 потоками заняло: 2.4458 секунд + +Параллельное умножение с 16 потоками заняло: 2.4899 секунд + +Размер матриц: 500x500 +Последовательное умножение заняло: 7.9416 секунд + +Параллельное умножение с 2 потоками заняло: 11.8896 секунд + +Параллельное умножение с 4 потоками заняло: 11.8901 секунд + +Параллельное умножение с 8 потоками заняло: 12.0230 секунд + +Параллельное умножение с 16 потоками заняло: 11.8548 секунд + + +## Выводы +Во всех случаях параллельный алгоритм выполняется дольше, чем последовательный. На рассматриваемых матрицах (100x100, 300x300, 500x500) последовательное умножение оказывается более выгодным. Это связано с тем, что накладные расходы на параллельное исполнение и синхронизацию между потоками превышают время, которое можно сэкономить за счёт многопоточности. + +## Запуск +Python main.py – threads 2 4 8 16 + +[Ссылка на демонстрацию работы программы](https://vk.com/video547368103_456239605?list=ln-ZAsjrYkuwZp6I4ghXC) \ No newline at end of file diff --git a/pupkov_alexey_lab_5/main.py b/pupkov_alexey_lab_5/main.py new file mode 100644 index 0000000..a46d05e --- /dev/null +++ b/pupkov_alexey_lab_5/main.py @@ -0,0 +1,92 @@ +from concurrent.futures import ThreadPoolExecutor +import time +import numpy as np +import argparse + +# Последовательное умножение +def matrix_multiply_sequential(A, B): + n = len(A) + C = [[0] * n for _ in range(n)] + + # Транспонируем матрицу B для оптимального доступа по строкам + B_T = [[B[j][i] for j in range(n)] for i in range(n)] + + for i in range(n): + A_row = A[i] + for j in range(n): + B_col = B_T[j] + sum_ij = 0 + for k in range(n): + sum_ij += A_row[k] * B_col[k] + C[i][j] = sum_ij + + return C + +# Вычисляет подматрицу C +def worker(A, B, C, start_row, end_row): + n = len(A) + for i in range(start_row, end_row): + for j in range(n): + sum_ij = 0 + for k in range(n): + sum_ij += A[i][k] * B[k][j] + C[i][j] = sum_ij + +# Параллельное умножение матриц +def matrix_multiply_parallel(A, B, num_threads): + n = len(A) + C = [[0] * n for _ in range(n)] + + # Разбиваем строки между потоками + rows_per_thread = n // num_threads + extra_rows = n % num_threads + row_splits = [rows_per_thread + (1 if i < extra_rows else 0) for i in range(num_threads)] + row_indices = [sum(row_splits[:i]) for i in range(num_threads + 1)] + + # Параллельно выполняем умножение подматриц + with ThreadPoolExecutor(max_workers=num_threads) as executor: + futures = [ + executor.submit(worker, A, B, C, row_indices[i], row_indices[i+1]) + for i in range(num_threads) + ] + for future in futures: + future.result() + + return C + +def benchmark(matrix_sizes, num_threads_list): + for size in matrix_sizes: + A = [[1] * size for _ in range(size)] # Матрицы, заполненные единицами для упрощения теста + B = [[1] * size for _ in range(size)] + print(f"\nРазмер матриц: {size}x{size}") + + # Бенчмарк для последовательного алгоритма + start_time = time.time() + C_seq = matrix_multiply_sequential(A, B) + sequential_time = time.time() - start_time + print(f"Последовательное умножение заняло: {sequential_time:.4f} секунд") + + # Бенчмарк для параллельного алгоритма + for num_threads in num_threads_list: + start_time = time.time() + C_par = matrix_multiply_parallel(A, B, num_threads) + parallel_time = time.time() - start_time + print(f"Параллельное умножение с {num_threads} потоками заняло: {parallel_time:.4f} секунд") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Запуск бенчмарков для умножения матриц.") + parser.add_argument( + "--threads", + type=int, + nargs="+", + required=True, + help="Список количества потоков для параллельного алгоритма (например, --threads 1 2 4 8)" + ) + args = parser.parse_args() + + # Задание размеров матриц для тестов + matrix_sizes = [100, 300, 500] + num_threads_list = args.threads # Получаем список потоков из аргументов командной строки + + # Запуск бенчмарка + benchmark(matrix_sizes, num_threads_list)