Merge pull request 'yakovlev_maxim_lab_5' (#428) from yakovlev_maxim_lab_5 into main
Reviewed-on: #428
This commit was merged in pull request #428.
This commit is contained in:
25
yakovlev_maxim_lab_5/README.md
Normal file
25
yakovlev_maxim_lab_5/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
## Лабораторная работа №5
|
||||
### Описание лабораторной работы
|
||||
Реализация последовательного и параллельного алгоритмов умножения квадратных матриц.
|
||||
- multiply_sequential - реализация последовательного алгоритма умножения матриц
|
||||
- multiply - реализация параллельного алгоритма умножения матриц
|
||||
- run_benchmark - метод запуска бенчмарка с выводом всей нужной информации
|
||||
### Результаты тестирования
|
||||
|
||||
Оба алгоритма работают с матрицами, заполненными целыми числами.
|
||||
Тестирование проведено для размеров: **100×100**, **300×300**, **500×500**.
|
||||
Количество потоков в параллельной версии: **2 и 4**.
|
||||
|
||||
| Размер матрицы | Последовательно | 2 потока | 4 потока |
|
||||
|----------------|------------------|-----------|-----------|
|
||||
| 100×100 | 0.518 с | 0.312 с | 0.312 с |
|
||||
| 300×300 | 13.932 с | 8.209 с | 8.416 с |
|
||||
| 500×500 | 65.786 с | 37.325 с | 37.595 с |
|
||||
|
||||
### Наблюдения
|
||||
1. **Параллельная версия работает значительно быстрее**, чем последовательная.
|
||||
2. Уже при **2 потоках** достигается почти максимальное ускорение.
|
||||
3. Переход от 2 к 4 потокам **практически не даёт прироста**
|
||||
4. Оптимальная конфигурация 1-2 потока. Дальнейшее увеличение потоков не дает преимущества
|
||||
|
||||
[Ссылка на видео](https://vkvideo.ru/video-233039857_456239021?list=ln-lVxcVooa9DTzJiFwrg)
|
||||
113
yakovlev_maxim_lab_5/matrix_multiplier.py
Normal file
113
yakovlev_maxim_lab_5/matrix_multiplier.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
def generate_matrix(n):
|
||||
return np.random.randint(1, 10, size=(n, n), dtype=np.int64)
|
||||
|
||||
class matrix_multiplier:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
#Последовательное умножение
|
||||
def multiply_sequential(self, A, B):
|
||||
n = len(A)
|
||||
C = [[0] * n for _ in range(n)]
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
for k in range(n):
|
||||
C[i][j] += A[i][k] * B[k][j]
|
||||
return C
|
||||
|
||||
#Умножение двух матриц с использованием заданного числа потоков
|
||||
def multiply(self, matrix_a: np.ndarray, matrix_b: np.ndarray, thread_num: int = 1) -> np.ndarray:
|
||||
|
||||
if matrix_a.shape[1] != matrix_b.shape[0]:
|
||||
raise ValueError("Невозможно умножить матрицы: несовпадение внутренних размеров")
|
||||
|
||||
n_rows = matrix_a.shape[0]
|
||||
n_cols = matrix_b.shape[1]
|
||||
result_matrix = np.zeros((n_rows, n_cols), dtype=np.int64)
|
||||
|
||||
# Определяем, сколько строк достанется каждому потоку
|
||||
base_rows = n_rows // thread_num
|
||||
extra = n_rows % thread_num
|
||||
|
||||
# Вычисление строк результирующей матрицы
|
||||
def _process_chunk(start_idx: int, end_idx: int):
|
||||
for i in range(start_idx, end_idx):
|
||||
for j in range(n_cols):
|
||||
acc = 0
|
||||
for k in range(matrix_a.shape[1]):
|
||||
acc += matrix_a[i, k] * matrix_b[k, j]
|
||||
result_matrix[i, j] = acc
|
||||
|
||||
active_threads = []
|
||||
current_start = 0
|
||||
|
||||
for tid in range(thread_num):
|
||||
# Первые 'extra' потоков получают на одну строку больше
|
||||
chunk_end = current_start + base_rows + (1 if tid < extra else 0)
|
||||
|
||||
if current_start < chunk_end: # Защита от пустых диапазонов
|
||||
thread = threading.Thread(
|
||||
target=_process_chunk,
|
||||
args=(current_start, chunk_end)
|
||||
)
|
||||
active_threads.append(thread)
|
||||
thread.start()
|
||||
|
||||
current_start = chunk_end
|
||||
|
||||
# Ожидаем завершения всех потоков
|
||||
for t in active_threads:
|
||||
t.join()
|
||||
|
||||
return result_matrix
|
||||
|
||||
def run_benchmark():
|
||||
sizes = [100, 300, 500]
|
||||
thread_counts = [2, 4]
|
||||
mm = matrix_multiplier()
|
||||
|
||||
for n in sizes:
|
||||
print(f"\n{'='*50}")
|
||||
print(f" Размер матрицы: {n}x{n}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
# Генерируем целочисленные матрицы
|
||||
A = generate_matrix(n)
|
||||
B = generate_matrix(n)
|
||||
|
||||
# --- Последовательная версия ---
|
||||
print("\n Последовательное умножение...")
|
||||
start = time.perf_counter()
|
||||
C_seq = mm.multiply_sequential(A, B)
|
||||
time_seq = time.perf_counter() - start
|
||||
print(f" Время: {time_seq:.3f} сек")
|
||||
|
||||
# --- Параллельная версия ---
|
||||
for threads in thread_counts:
|
||||
print(f"\n Параллельное умножение ({threads} потоков)...")
|
||||
start = time.perf_counter()
|
||||
C_par = mm.multiply(A, B, thread_num=threads)
|
||||
time_par = time.perf_counter() - start
|
||||
print(f" Время: {time_par:.3f} сек")
|
||||
|
||||
# Проверка корректности
|
||||
if np.array_equal(C_seq, C_par):
|
||||
print("Результаты совпадают!")
|
||||
else:
|
||||
print("Ошибка: результаты не совпадают!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
A = generate_matrix(100)
|
||||
B = generate_matrix(100)
|
||||
|
||||
mm = matrix_multiplier()
|
||||
|
||||
C = mm.multiply_sequential(A,B)
|
||||
|
||||
run_benchmark()
|
||||
Reference in New Issue
Block a user