diff --git a/kochkareva_elizaveta_lab_5/README.md b/kochkareva_elizaveta_lab_5/README.md new file mode 100644 index 0000000..6c17061 --- /dev/null +++ b/kochkareva_elizaveta_lab_5/README.md @@ -0,0 +1,103 @@ + +# Лабораторная работа 5. + +### Задание + +**Задачи**: + +Реализовать умножение двух больших квадратных матриц. +Сделать два алгоритма: обычный и параллельный (задание со * - реализовать это в рамках одного алгоритма). В параллельном алгоритме предусмотреть ручное задание количества потоков (число потоков = 1 как раз и реализует задание со *), каждый из которых будет выполнять умножение элементов матрицы в рамках своей зоны ответственности. + + +### Как запустить лабораторную работу +В директории с файлом характеристик docker-compose.yaml выполнить команду: +``` +docker-compose -f docker-compose.yaml up +``` + +### Описание лабораторной работы + +Для реализации параллельного умножения матриц с использованием многопоточности создадим несколько функций: + +1. Функция `multiplication_rows(row, matrix_b)` + +Данная функция используется для перемножения входной строки row на матрицу matrix_b и возвращает результат умножения. + +```python +def multiplication_rows(row, matrix_b): + return np.dot(row, matrix_b) +``` + +2. Функция `parallel_matrix_multiplication(matrix_a, matrix_b, num_threads)` + +Данная функция Принимает две матрицы `matrix_a` и `matrix_b`, а также количество потоков `num_threads`, которое ровно количеству строк в первой матрице. Также осуществляется проверка на то, что размеры матрицы совместимы. + +Затем создается пул потоков с использованием `concurrent.futures.ThreadPoolExecutor` и устанавливает максимальное количество потоков равным `num_threads`. Запускается таймер для измерения времени выполнения умножения матриц. Создается список `results` и +для каждой строки матрицы `matrix_a` запускает функцию `multiplication_rows` в отдельном потоке с помощью `executor.submit()`. Результаты сортируются по индексу строки и объединяются в матрицу в правильном порядке с помощью `np.vstack()`. Завершается таймер и происходит замер времени, затраченного на выполнение задачи. + +```python +def parallel_matrix_multiplication(matrix_a, matrix_b, num_threads): + num_rows_a, num_cols_a = matrix_a.shape + num_rows_b, num_cols_b = matrix_b.shape + assert num_cols_a == num_rows_b, "Размеры матриц несовместимы" + + with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: + start_time = time.time() + results = [] + for i in range(num_rows_a): + result = executor.submit(multiplication_rows, matrix_a[i], matrix_b) + results.append((i, result)) + sorted_results = sorted(results, key=lambda x: x[0]) + result_matrix = np.vstack( + [result.result() for _, result in sorted_results]) + end_time = time.time() + + execution_time = end_time - start_time + return result_matrix, execution_time +``` + +3. Функция `test(parallel)` + +Данная функция создает две матрицы с фиксированными значениями. И происходит разбиение на два алгоритма вычисления умножения матриц: обычный и параллельный.Если `parallel` равно True, вызывает `parallel_matrix_multiplication()` с `num_threads=2`, иначе с `num_threads=1`. + +4. Функции `matrix100x100(parallel)`, `matrix300x300(parallel)` и `matrix500x500(parallel)` + +В данных функция создается пара случайных матриц размером *100x100*, *300x300* и *500x500* со значениями от 0 до 100. + +Если `parallel` равно `True`, вызывают `parallel_matrix_multiplication()` с `num_threads` равным размеру матрицы, иначе с `num_threads=1`. +Выводят результат умножения и время выполнения. + +Пример функции для матрицы размером *100x100*: + +```python +def matrix100x100(parallel): + a = np.random.randint(0, 100, size=(100, 100)) + b = np.random.randint(0, 100, size=(100, 100)) + if parallel: + result = parallel_matrix_multiplication(a, b, num_threads=100) + else: + result = parallel_matrix_multiplication(a, b, num_threads=1) + print("Результат умножения:") + print(result[0]) + print("Время выполнения: " + str(result[1]) + " с") +``` + +#### Результаты выполнения последовательного и параллельного алгоритма на умножение двух матриц размером 100x100, 300x300, 500x500 элементов. +Результат перемножения матриц 100х100: + +![Результат перемножения матриц 100х100](matrix100x100.jpg) + +Результат перемножения матриц 300х300: + +![Результат перемножения матриц 300х300](matrix300x300.jpg) + +езультат перемножения матриц 500х500: + +![Результат перемножения матриц 500х500](matrix500x500.jpg) + +Таким образом, можно сделать вывод о том, что умножение матрицы параллельным способом значительно ускоряет процесс выполнения по сравнению с обычным способом на больших объемах данных. В случае матрицы размером 500х500, время выполнения параллельного умножения составляет всего 0.03999614715576172 с, в то время как обычный способ занимает 0.10399985313415527 с. Однако случае матрицы размером 100х100, время выполнения параллельного умножения составляет 0.008999347686767578 с, в то время как обычный способ занимает 0.006066799163818359 с, и перемножение обычным алгоритмом является более выгодным по временнным затратам, чем паралелльным. +Параллельное умножение матрицы может быть полезным в случаях, когда требуется обработать большие объемы данных и ускорить процесс вычислений. + +### Видео + +https://disk.yandex.ru/i/uJzemvtUlgmR2g \ No newline at end of file diff --git a/kochkareva_elizaveta_lab_5/main.py b/kochkareva_elizaveta_lab_5/main.py new file mode 100644 index 0000000..0721088 --- /dev/null +++ b/kochkareva_elizaveta_lab_5/main.py @@ -0,0 +1,98 @@ +import numpy as np +import concurrent.futures +import time + + +def multiplication_rows(row, matrix_b): + return np.dot(row, matrix_b) + + +def parallel_matrix_multiplication(matrix_a, matrix_b, num_threads): + num_rows_a, num_cols_a = matrix_a.shape + num_rows_b, num_cols_b = matrix_b.shape + assert num_cols_a == num_rows_b, "Размеры матриц несовместимы" + + with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: + start_time = time.time() + results = [] + for i in range(num_rows_a): + result = executor.submit(multiplication_rows, matrix_a[i], matrix_b) + results.append((i, result)) + sorted_results = sorted(results, key=lambda x: x[0]) + result_matrix = np.vstack( + [result.result() for _, result in sorted_results]) + end_time = time.time() + + execution_time = end_time - start_time + return result_matrix, execution_time + + +def test(parallel): + a = np.array([[12, 42, 64], + [38, 4, 21]]) + b = np.array([[35, 2], + [64, 41], + [5, 33]]) + if parallel: + result = parallel_matrix_multiplication(a, b, num_threads=2) + else: + result = parallel_matrix_multiplication(a, b, num_threads=1) + print("Результат умножения:") + print(result[0]) + print("Время выполнения: " + str(result[1]) + " с") + + +def matrix100x100(parallel): + a = np.random.randint(0, 100, size=(100, 100)) + b = np.random.randint(0, 100, size=(100, 100)) + if parallel: + result = parallel_matrix_multiplication(a, b, num_threads=100) + else: + result = parallel_matrix_multiplication(a, b, num_threads=1) + print("Результат умножения:") + print(result[0]) + print("Время выполнения: " + str(result[1]) + " с") + + +def matrix300x300(parallel): + a = np.random.randint(0, 100, size=(300, 300)) + b = np.random.randint(0, 100, size=(300, 300)) + if parallel: + result = parallel_matrix_multiplication(a, b, num_threads=300) + else: + result = parallel_matrix_multiplication(a, b, num_threads=1) + print("Результат умножения:") + print(result[0]) + print("Время выполнения: " + str(result[1]) + " с") + + +def matrix500x500(parallel): + a = np.random.randint(0, 100, size=(500, 500)) + b = np.random.randint(0, 100, size=(500, 500)) + if parallel: + result = parallel_matrix_multiplication(a, b, num_threads=500) + else: + result = parallel_matrix_multiplication(a, b, num_threads=1) + print("Результат умножения:") + print(result[0]) + print("Время выполнения: " + str(result[1]) + " с") + + +if __name__ == '__main__': + # test(parallel=True) + # print("Матрица 100x100:") + # print("Результат умножения параллельно:") + # matrix100x100(parallel=True) + # print("Результат умножения обычным способом:") + # matrix100x100(parallel=False) + # print("Матрица 300x300:") + # print("Результат умножения параллельно:") + # matrix300x300(parallel=True) + # print("Результат умножения обычным способом:") + # matrix300x300(parallel=False) + print("Матрица 500x500:") + print("Результат умножения параллельно:") + matrix500x500(parallel=True) + print("Результат умножения обычным способом:") + matrix500x500(parallel=False) + diff --git a/kochkareva_elizaveta_lab_5/matrix100x100.jpg b/kochkareva_elizaveta_lab_5/matrix100x100.jpg new file mode 100644 index 0000000..2743c47 Binary files /dev/null and b/kochkareva_elizaveta_lab_5/matrix100x100.jpg differ diff --git a/kochkareva_elizaveta_lab_5/matrix300x300.jpg b/kochkareva_elizaveta_lab_5/matrix300x300.jpg new file mode 100644 index 0000000..e6de38a Binary files /dev/null and b/kochkareva_elizaveta_lab_5/matrix300x300.jpg differ diff --git a/kochkareva_elizaveta_lab_5/matrix500x500.jpg b/kochkareva_elizaveta_lab_5/matrix500x500.jpg new file mode 100644 index 0000000..36b3745 Binary files /dev/null and b/kochkareva_elizaveta_lab_5/matrix500x500.jpg differ