Init
This commit is contained in:
commit
c394677d07
59 files changed
+5149
No files matched your search
@@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Лабораторная работа 8: распределённые вычисления (mpi4py).
|
||||
|
||||
Заполните функции ниже. ИНТЕРФЕЙСЫ МЕНЯТЬ НЕЛЬЗЯ — по ним работают
|
||||
автотесты (tests/test_lab08.py; если mpi4py не установлен — тесты
|
||||
пропускаются, но работа сдаётся с запуском через mpiexec).
|
||||
|
||||
Запуск из корня курса:
|
||||
python lab08_mpi/solution.py (один ранг — отладка)
|
||||
mpiexec -n 4 python lab08_mpi/solution.py (4 ранга — замеры)
|
||||
"""
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
from common.tasks import TASKS, get_variant
|
||||
|
||||
try:
|
||||
from mpi4py import MPI
|
||||
HAVE_MPI = True
|
||||
except ImportError:
|
||||
HAVE_MPI = False
|
||||
|
||||
# TODO: впишите свой номер в журнале (1..20)
|
||||
VARIANT_NUMBER = 0
|
||||
|
||||
|
||||
def _group_for_ranks(chunks, size):
|
||||
"""Распределить куски по рангам round-robin: список из size групп.
|
||||
|
||||
scatter ожидает список ровно из size элементов, а естественных кусков
|
||||
может быть другое число (например, nqueens всегда даёт N кусков) —
|
||||
поэтому группируем.
|
||||
"""
|
||||
# TODO: groups = [[] for _ in range(size)]; groups[i % size].append(ch)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def mpi_run(task_name: str, params: dict):
|
||||
"""CPU-задача на всех рангах: scatter групп -> kernel -> gather -> combine.
|
||||
|
||||
Схема:
|
||||
ранг 0: build -> split(data, size) -> _group_for_ranks
|
||||
scatter раздаёт ГРУППЫ кусков (по одной на ранг)
|
||||
каждый ранг: my_partials = [task["kernel"](ch) for ch in my_group]
|
||||
gather собирает списки частичных результатов
|
||||
ранг 0: развернуть в плоский список -> combine ОДИН раз -> checksum
|
||||
|
||||
Функции пиккелить не нужно (в отличие от multiprocessing) — только данные.
|
||||
|
||||
Для задачи 'life': данные — (grid, steps), параллелятся ПОКОЛЕНИЯ.
|
||||
Ранг 0 держит текущее поле; каждый шаг: scatter полос -> kernel ->
|
||||
gather -> combine -> новое поле. Число шагов раздаётся через bcast.
|
||||
"""
|
||||
if not HAVE_MPI:
|
||||
raise RuntimeError("mpi4py не установлен (см. методичку, раздел Установка)")
|
||||
task = TASKS[task_name]
|
||||
comm = MPI.COMM_WORLD
|
||||
rank = comm.Get_rank()
|
||||
size = comm.Get_size()
|
||||
|
||||
if task_name == "life":
|
||||
# TODO: цикл по поколениям (см. схему выше); в конце bcast поля
|
||||
# с ранга 0 всем, чтобы checksum был у всех рангов
|
||||
raise NotImplementedError
|
||||
|
||||
# TODO: общий случай по схеме выше; вернуть checksum на ранге 0 (None на других)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def mpi_pi(N: int) -> float:
|
||||
"""π методом Монте-Карло на всех рангах (reduce).
|
||||
|
||||
Каждый ранг бросает N//size точек с зерном random.Random(100 + rank).
|
||||
reduce(MPI.SUM) сворачивает попадания на ранге 0.
|
||||
Вернуть π (на ранге 0 — вычисленное значение, на остальных 0.0).
|
||||
"""
|
||||
# TODO: my_n = N // size; цикл бросков; comm.reduce(inside, op=MPI.SUM, root=0)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def main():
|
||||
if not HAVE_MPI:
|
||||
print("mpi4py не установлен. Инструкция — в методичке, раздел «Установка».")
|
||||
print("Кратко (Windows): MS-MPI runtime + pip install mpi4py")
|
||||
sys.exit(1)
|
||||
|
||||
v = get_variant(VARIANT_NUMBER)
|
||||
comm = MPI.COMM_WORLD
|
||||
rank = comm.Get_rank()
|
||||
size = comm.Get_size()
|
||||
|
||||
if rank == 0:
|
||||
print(f"Вариант {v['variant']}: {v['describe']}")
|
||||
print(f"Запущено рангов: {size}\n")
|
||||
|
||||
# --- pi через reduce ------------------------------------------------------
|
||||
pi = mpi_pi(1_000_000)
|
||||
if rank == 0:
|
||||
print(f"π ≈ {pi:.6f} (Монте-Карло, 1 000 000 бросков суммарно)")
|
||||
|
||||
# --- основная задача --------------------------------------------------------
|
||||
comm.barrier()
|
||||
t0 = time.perf_counter()
|
||||
checksum = mpi_run(v["task_name"], v["cpu_params"])
|
||||
dt = time.perf_counter() - t0
|
||||
|
||||
if rank == 0:
|
||||
from common.tasks import run_sequential
|
||||
ref, _ = run_sequential(v["task_name"], v["cpu_params"], parts=1)
|
||||
ok = checksum == ref or (isinstance(checksum, float) and
|
||||
abs(checksum - ref) < 1e-6 * max(1, abs(ref)))
|
||||
print(f"задача: {dt:.3f} c, checksum совпадает с последовательным: {ok}")
|
||||
print("\nЗапустите при разном числе рангов для таблицы T(p):")
|
||||
print(" mpiexec -n 1 python lab08_mpi/solution.py")
|
||||
print(" mpiexec -n 2 python lab08_mpi/solution.py")
|
||||
print(" mpiexec -n 4 python lab08_mpi/solution.py")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user