diff --git a/alexandrov_dmitrii_lab_5/matrix.py b/alexandrov_dmitrii_lab_5/matrix.py new file mode 100644 index 0000000..71a1468 --- /dev/null +++ b/alexandrov_dmitrii_lab_5/matrix.py @@ -0,0 +1,109 @@ +import numpy as np +import time +from concurrent.futures import ProcessPoolExecutor + +benchmark = {} +m_a = [] +m_b = [] +m_c = [] + + +# лучше не надо +def do_multiplication(size): + global m_a + global m_b + global m_c + + m_a = np.random.randint(10, size=(size, size)) + m_b = np.random.randint(10, size=(size, size)) + m_c = np.zeros(shape=(size, size)) + bt = m_b.transpose() + + start_time = time.time() + for i in range(size): + for j in range(size): + for k in range(size): + m_c[i][j] = m_c[i][j] + m_a[i][k] * bt[j][k] + + return time.time() - start_time + + +def multiply_row(a, bt, c, size): + for j in range(size): + c[j] = sum(el_a * el_b for el_a, el_b in zip(a, bt[j])) + return c + + +def do_multiplication_parallel(size, proc_num): + global m_a + global m_b + global m_c + + if proc_num > 61: + proc_num = 61 + + m_a = np.random.randint(10, size=(size, size)) + m_b = np.random.randint(10, size=(size, size)) + m_c = np.zeros(shape=(size, size)) + bt = m_b.transpose() + + start_time = time.time() + with ProcessPoolExecutor(max_workers=proc_num) as executor: + results = [executor.submit(multiply_row, m_a[i], bt, m_c[i], size) for i in range(size)] + m_c = [future.result() for future in results] + + return time.time() - start_time + + +def do_research(): + benchmark['size=100, proc_num=1: '] = do_multiplication_parallel(100, 1) + benchmark['size=300, proc_num=1: '] = do_multiplication_parallel(300, 1) + benchmark['size=500, proc_num=1: '] = do_multiplication_parallel(500, 1) + benchmark['size=100, proc_num=10: '] = do_multiplication_parallel(100, 10) + benchmark['size=300, proc_num=10: '] = do_multiplication_parallel(300, 10) + benchmark['size=500, proc_num=10: '] = do_multiplication_parallel(500, 10) + benchmark['size=100, proc_num=100: '] = do_multiplication_parallel(100, 100) + benchmark['size=300, proc_num=100: '] = do_multiplication_parallel(300, 100) + benchmark['size=500, proc_num=100: '] = do_multiplication_parallel(500, 100) + print(benchmark) + + +if __name__ == '__main__': + do_research() + + +def get_results(size, proc_num): + global m_a + global m_b + global m_c + + res = "time: " + res = res + str(do_multiplication_parallel(size, proc_num)) + + res = res + "
" + for i in range(size): + res = res + "

" + for a in range(size): + res = res + str(m_a[i][a]) + ", " + res = res + " " + for b in range(size): + res = res + str(m_b[i][b]) + ", " + res = res + " " + for c in range(size): + res = res + str(m_c[i][c]) + ", " + res = res + "

" + + return res + + +def get_benchmark(): + global benchmark + + if len(benchmark) == 0: + do_research() + + res = '' + for key, val in benchmark.items(): + res = res + "

" + key + str(val) + "

" + + return res diff --git a/alexandrov_dmitrii_lab_5/readme.md b/alexandrov_dmitrii_lab_5/readme.md new file mode 100644 index 0000000..b89568c --- /dev/null +++ b/alexandrov_dmitrii_lab_5/readme.md @@ -0,0 +1,37 @@ +## Задание +Создать программу, производящую параллельные умножения реализовать двух больших квадратных матриц. + +## Выполнение +Программа состоит из модуля-сервиса service и модуля для вычислений matrix. +В модуле для вычислений реализовано: +* метод do_multiplication, который в обычном режиме одним потоком умножает матрицы. Не используется, т.к. слишком медленный. +* метод multiply_row, который получает строку матрицы A, транспонированную матрицу B, строку выходной матрицы C, размер матриц и заполняет эту строку проходя по строкам транспонированной матрицы B. Возвращает заполненную строку. +* метод do_multiplication_parallel, использующий предыдущий, который получает размер матриц и количество процессов, генерирует случайные матрицы заданного размера и производит вычисления. +* метод прогона эксперимента с заполнением данных результатами. Методы интерфейса доступа. + +Способ вычисления: в методе do_multiplication_parallel матрица B транспонируется, создаётся объект ProcessPoolExecutor с переданным количеством процессов, который их создаёт и распределяет по ним строки вычисляемой матрицы, а именно передавая им метод multiply_row. +Если передать методу число 1 в качестве количества процессов, то соответственно вычисление произойдёт в одном потоке. + +## Результаты +Был создан Flask сервис, позволяющий получать результаты умножения случайных квадратных матриц. +Возможно задать размер и количество процессов. + +Дополнительно возможно провести эксперимент и получить результаты умножения матриц размера 100, 300 и 500 одним, десятью и ста процессами. + +Поскольку распараллеливание основано на процессах, а в системе имеется 6 ядер (+2 виртуальных), то максимальное увеличение производительности будет достигнуто при выборе такого количества процессов. +Более 61 потока урезается до 61 из-за системных ограничений. +Как видно из бенчмарка: +* во всех случаях скорость максимальна при выборе 10 процессов, т.е. при ближайшем к 8 числу процессов. +* в случае 100 процессов из-за издержек создания процессов малые матрицы обрабатываются на порядок дольше, нежели одним процессом. +* в случае 100 процессов благодаря распараллеливанию скорость обработки оказалась в 3 раза выше скорости обработки одним процессом. + +Результаты: +![matrix5x5](screens/get5.png) + +![matrix15x15](screens/get15.png) + +Бенчмарк: +![bench](screens/bench.png) + +## Ссылка на видео +https://drive.google.com/file/d/1_bIyLL8YGwDePwWdCFk4KxntJip6mP0t/view?usp=drive_link \ No newline at end of file diff --git a/alexandrov_dmitrii_lab_5/screens/bench.png b/alexandrov_dmitrii_lab_5/screens/bench.png new file mode 100644 index 0000000..2ffefcf Binary files /dev/null and b/alexandrov_dmitrii_lab_5/screens/bench.png differ diff --git a/alexandrov_dmitrii_lab_5/screens/get15.png b/alexandrov_dmitrii_lab_5/screens/get15.png new file mode 100644 index 0000000..4d25d26 Binary files /dev/null and b/alexandrov_dmitrii_lab_5/screens/get15.png differ diff --git a/alexandrov_dmitrii_lab_5/screens/get5.png b/alexandrov_dmitrii_lab_5/screens/get5.png new file mode 100644 index 0000000..022b4b5 Binary files /dev/null and b/alexandrov_dmitrii_lab_5/screens/get5.png differ diff --git a/alexandrov_dmitrii_lab_5/service.py b/alexandrov_dmitrii_lab_5/service.py new file mode 100644 index 0000000..3b1f5bd --- /dev/null +++ b/alexandrov_dmitrii_lab_5/service.py @@ -0,0 +1,35 @@ +from flask import Flask, redirect, request, render_template +import matrix + +app = Flask(__name__, template_folder='') + +results = '' + + +@app.route('/') +def home(): + global results + return render_template("template.html", results_html=results) + + +@app.route('/do') +def do(): + global results + + data = request.args + results = matrix.get_results(int(data['size']), int(data['proc_num'])) + + return redirect("/") + + +@app.route('/benchmark') +def benchmark(): + global results + + results = matrix.get_benchmark() + + return redirect("/") + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8082) diff --git a/alexandrov_dmitrii_lab_5/template.html b/alexandrov_dmitrii_lab_5/template.html new file mode 100644 index 0000000..c999367 --- /dev/null +++ b/alexandrov_dmitrii_lab_5/template.html @@ -0,0 +1,18 @@ + + + + + Matrix + + +
+ + + +
+
+ +
+ {{results_html|safe}} + + \ No newline at end of file