medvedkov_andrey_lab_5 is ready

This commit is contained in:
MaD
2025-12-11 22:32:01 +04:00
parent 716d205ebb
commit c4c3c449ed
2 changed files with 286 additions and 0 deletions

View File

@@ -0,0 +1,210 @@
import random
import time
import math
from concurrent.futures import ProcessPoolExecutor, as_completed
import argparse
def generate_random_matrix(n: int, seed: int | None = None) -> list[list[float]]:
"""Генерация квадратной матрицы n x n со случайными целыми значениями [0, 10)."""
rng = random.Random(seed)
return [[rng.randint(0, 9) for _ in range(n)] for _ in range(n)]
def multiply_sequential(a: list[list[float]], b: list[list[float]]) -> list[list[float]]:
"""Классический последовательный алгоритм умножения матриц O(n^3)."""
n = len(a)
if n == 0 or len(b) != n or len(b[0]) != n:
raise ValueError("Матрицы должны быть квадратными и одинакового размера")
# Результирующая матрица, заполненная нулями
c = [[0.0 for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
s = 0.0
# скалярное произведение i-й строки A и j-го столбца B
for k in range(n):
s += a[i][k] * b[k][j]
c[i][j] = s
return c
def _multiply_chunk(a: list[list[float]],
b: list[list[float]],
start_row: int,
end_row: int) -> tuple[int, list[list[float]]]:
"""
Вспомогательная функция для процесса:
считает строки [start_row, end_row) результата и возвращает их.
Возвращаем также start_row, чтобы потом собрать всё в правильном порядке.
"""
n = len(a)
rows = []
for i in range(start_row, end_row):
row_res = []
for j in range(n):
s = 0.0
for k in range(n):
s += a[i][k] * b[k][j]
row_res.append(s)
rows.append(row_res)
return start_row, rows
def multiply(a: list[list[float]],
b: list[list[float]],
workers: int = 1) -> list[list[float]]:
"""
Универсальное умножение матриц:
- workers = 1 -> фактически последовательный режим
- workers >= 2 -> параллельный режим через ProcessPoolExecutor
Логика "задания со *": один алгоритм, параметризованный количеством "потоков" (воркеров).
"""
n = len(a)
if n == 0 or len(b) != n or len(b[0]) != n:
raise ValueError("Матрицы должны быть квадратными и одинакового размера")
if workers <= 1:
# последовательный режим
return multiply_sequential(a, b)
# параллельный режим: делим строки результата между воркерами
result = [[0.0 for _ in range(n)] for _ in range(n)]
rows_per_worker = math.ceil(n / workers)
tasks = []
with ProcessPoolExecutor(max_workers=workers) as executor:
for w in range(workers):
start_row = w * rows_per_worker
end_row = min(start_row + rows_per_worker, n)
if start_row >= end_row:
break # воркеров больше, чем строк
fut = executor.submit(_multiply_chunk, a, b, start_row, end_row)
tasks.append(fut)
for fut in as_completed(tasks):
start_row, rows = fut.result()
for offset, row in enumerate(rows):
result[start_row + offset] = row
return result
def matrices_equal(m1: list[list[float]],
m2: list[list[float]],
eps: float = 1e-6) -> bool:
"""Проверка равенства двух матриц поэлементно с допуском eps."""
if len(m1) != len(m2) or len(m1[0]) != len(m2[0]):
return False
n = len(m1)
m = len(m1[0])
for i in range(n):
for j in range(m):
if abs(m1[i][j] - m2[i][j]) > eps:
return False
return True
def benchmark_one_size(n: int, worker_counts: list[int]) -> None:
"""Запуск серии бенчмарков для одной размерности матрицы."""
print(f"\nРазмер матрицы: {n}x{n}")
print("-" * 40)
# фиксируем seed, чтобы A и B были одинаковыми для разных запусков
a = generate_random_matrix(n, seed=42)
b = generate_random_matrix(n, seed=123)
# последовательное умножение
t0 = time.perf_counter()
c_seq = multiply(a, b, workers=1)
t1 = time.perf_counter()
seq_ms = (t1 - t0) * 1000.0
print(f"Последовательный алгоритм (1 процесс): {seq_ms:.3f} ms")
for w in worker_counts:
if w <= 1:
continue
# небольшой прогрев (не меряем)
_ = multiply(a, b, workers=w)
t0 = time.perf_counter()
c_par = multiply(a, b, workers=w)
t1 = time.perf_counter()
par_ms = (t1 - t0) * 1000.0
ok = matrices_equal(c_seq, c_par)
speedup = seq_ms / par_ms if par_ms > 0 else float('inf')
status = "OK" if ok else "FAIL"
print(f"Параллельный алгоритм ({w} процессов): {par_ms:.3f} ms "
f"(ускорение: {speedup:.2f}x, корректность: {status})")
def run_default_benchmarks():
sizes = [100, 300, 500]
workers = [1, 2, 4]
print("Бенчмарк умножения квадратных матриц (Python)")
print("============================================")
for n in sizes:
benchmark_one_size(n, workers)
def parse_args():
parser = argparse.ArgumentParser(
description="Умножение квадратных матриц (последовательно и параллельно)."
)
parser.add_argument(
"-n", "--size",
type=int,
help="размер квадратной матрицы n (по умолчанию запускаются все: 100, 300, 500)"
)
parser.add_argument(
"-w", "--workers",
type=int,
help="количество процессов (workers) для параллельного режима; "
"если не задано, выполняются бенчмарки для 1, 2, 4"
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
if args.size is not None and args.workers is not None:
# одиночный запуск
n = args.size
w = args.workers
print(f"Одиночный запуск: размер {n}x{n}, workers={w}")
a = generate_random_matrix(n, seed=42)
b = generate_random_matrix(n, seed=123)
t0 = time.perf_counter()
c_seq = multiply(a, b, workers=1)
t1 = time.perf_counter()
seq_ms = (t1 - t0) * 1000.0
t0 = time.perf_counter()
c_par = multiply(a, b, workers=w)
t1 = time.perf_counter()
par_ms = (t1 - t0) * 1000.0
ok = matrices_equal(c_seq, c_par)
speedup = seq_ms / par_ms if par_ms > 0 else float('inf')
print(f"Последовательный (1 процесс): {seq_ms:.3f} ms")
print(f"Параллельный ({w} процессов): {par_ms:.3f} ms")
print(f"Ускорение: {speedup:.2f}x")
print(f"Корректность: {'OK' if ok else 'FAIL'}")
else:
# дефолтные бенчмарки
run_default_benchmarks()

View File

@@ -0,0 +1,76 @@
## Как запустить
Требуется **Python 3** и файл `matrix_mul.py` в текущей папке.
Полный прогон бенчмарков (матрицы 100×100, 300×300, 500×500; 1, 2, 4 процесса):
```bash
python matrix_mul.py
```
Запуск одного эксперимента с заданным размером и числом процессов:
```bash
Копировать код
python matrix_mul.py -n <размер> -w <число_процессов>
```
## пример:
```python matrix_mul.py -n 300 -w 4```
Используемые технологии
Язык: Python 3
Стандартная библиотека:
- random — генерация матриц;
- time — измерение времени;
- math — расчёт размеров блоков строк;
- argparse — аргументы командной строки;
- concurrent.futures.ProcessPoolExecutor — параллельные вычисления в нескольких процессах.
## Что делает программа
Генерирует две квадратные матрицы размера n×n со случайными значениями.
Умножает их:
- последовательно (1 процесс);
- параллельно (несколько процессов), деля строки результата между процессами.
Использует одну функцию multiply(a, b, workers):
- workers = 1 — последовательный режим;
- workers > 1 — параллельный режим (выполнение задания со *).
Замеряет время работы обоих вариантов, считает ускорение и выводит результаты в консоль.
## Результаты бенчмарка
Размер матрицы: 100x100
----------------------------------------
- Последовательный алгоритм (1 процесс): 59.812 ms
- Параллельный алгоритм (2 процессов): 135.846 ms (ускорение: 0.44x, корректность: OK)
- Параллельный алгоритм (4 процессов): 148.590 ms (ускорение: 0.40x, корректность: OK)
Размер матрицы: 300x300
----------------------------------------
- Последовательный алгоритм (1 процесс): 1741.963 ms
- Параллельный алгоритм (2 процессов): 1048.928 ms (ускорение: 1.66x, корректность: OK)
- Параллельный алгоритм (4 процессов): 579.664 ms (ускорение: 3.01x, корректность: OK)
Размер матрицы: 500x500
----------------------------------------
- Последовательный алгоритм (1 процесс): 8510.345 ms
- Параллельный алгоритм (2 процессов): 4915.916 ms (ускорение: 1.73x, корректность: OK)
- Параллельный алгоритм (4 процессов): 2448.955 ms (ускорение: 3.48x, корректность: OK)
# ссылка на видео:
https://vkvideo.ru/video-234070899_456239021?list=ln-v5QLhJLR9cEzcnI3Og