140 lines
7.0 KiB
Markdown
140 lines
7.0 KiB
Markdown
# Лабораторная работа 8. Распределённые вычисления (MPI)
|
||
|
||
**Время на работу:** 2–3 пары. **Зависимости:** лабы 1, 4.
|
||
|
||
## Цель работы
|
||
|
||
Освоить модель передачи сообщений (MPI) — стандарт распределённых вычислений,
|
||
на котором работают суперкомпьютеры. Понять отличие «общей памяти»
|
||
(потоки/процессы Python) от «обмена сообщениями» между независимыми узлами.
|
||
|
||
## Теория
|
||
|
||
### Модель MPI
|
||
|
||
MPI (Message Passing Interface) — набор программ на нескольких **узлах**
|
||
(машинах или процессах), обменивающихся сообщениями. Нет общей памяти: любые
|
||
данные передаются явно, функциями отправки/приёма. Программа запускается
|
||
одновременно на N «рангах» (процессах):
|
||
|
||
```bat
|
||
mpiexec -n 4 python script.py # 4 копии script.py
|
||
```
|
||
|
||
Каждая копия узнаёт, кто она:
|
||
|
||
```python
|
||
from mpi4py import MPI
|
||
comm = MPI.COMM_WORLD # «мир» — все ранги
|
||
rank = comm.Get_rank() # мой номер: 0..size-1
|
||
size = comm.Get_size() # всего рангов
|
||
```
|
||
|
||
Ранг 0 обычно координатор: он делит работу и собирает результат.
|
||
|
||
### Операции: «точка-точка» и коллективные
|
||
|
||
**Точка-точка** (ранг ↔ ранг):
|
||
|
||
```python
|
||
comm.send(data, dest=1, tag=0) # блокирующая отправка
|
||
data = comm.recv(source=0, tag=0) # блокирующий приём
|
||
```
|
||
|
||
**Коллективные** (все ранги сразу — проще и обычно быстрее):
|
||
|
||
| Операция | Что делает |
|
||
|---|---|
|
||
| `bcast` | один раздает всем (broadcast) |
|
||
| `scatter` | список режется на части: ранг i получает i-ю часть |
|
||
| `gather` | части собираются в список на одном ранге |
|
||
| `reduce` | части сворачиваются операцией (sum/max/…) на одном ранге |
|
||
| `barrier` | все ждут, пока все дойдут до этой точки |
|
||
|
||
### Схема параллельной задачи в MPI
|
||
|
||
```
|
||
ранг 0: build -> split -> scatter ─┐
|
||
ранги 1..N: получают кусок │-> kernel -> gather -> combine -> checksum
|
||
ранг 0: собирает ┘
|
||
```
|
||
|
||
Это ровно ваша схема split→kernel→combine из лаб 1–4, только обмен данными —
|
||
через сеть/IPC, а не общую память.
|
||
|
||
### Установка (Windows)
|
||
|
||
1. MS-MPI: скачать и установить **msmpisetup.exe** и **msmpires.msi**
|
||
(https://www.microsoft.com/en-us/download/details.aspx?id=105289).
|
||
2. `pip install mpi4py`
|
||
3. Проверка: `mpiexec -n 2 python -c "from mpi4py import MPI; print(MPI.COMM_WORLD.Get_rank())"`
|
||
|
||
На macOS/Linux: `brew install open-mpi` / `apt install libopenmpi-dev`,
|
||
затем `pip install mpi4py`, запуск `mpiexec` (или `mpirun`).
|
||
|
||
Важно: запускать `mpiexec -n 4` — тогда `solution.py` выполнится 4 раза
|
||
параллельно. Запуск без mpiexec допустим: программа работает как один ранг
|
||
(полезно для отладки и автотестов).
|
||
|
||
## Задание
|
||
|
||
Заполните `solution.py`:
|
||
|
||
1. **`mpi_run(task_name, params)`** — параллельное выполнение вашей CPU-задачи:
|
||
ранг 0 строит данные и делит на `size` кусков, `scatter` раздаёт,
|
||
каждый ранг считает `kernel` (используя `dispatch_kernel` не нужно —
|
||
в MPI функции не пиккелятся, только данные), `gather` собирает,
|
||
ранг 0 возвращает checksum. Для задачи «Жизнь» — цикл по поколениям
|
||
(см. подсказку в заготовке).
|
||
2. **`mpi_pi(N)`** — учебный пример: π методом Монте-Карло на всех рангах,
|
||
сборка через `reduce` (сумма попаданий). Каждый ранг использует
|
||
`random.Random(100 + rank)` — свои броски.
|
||
3. **`main()`** — checksum и время `mpi_run` на текущем числе рангов
|
||
(`size`), печать с ранга 0. Запустите при `-n 1, 2, 4` — получите T(p).
|
||
|
||
### Ожидаемые результаты
|
||
|
||
- checksum совпадает с последовательным запуском (лаба 1).
|
||
- T(p) на одном компьютере ≈ результатам multiprocessing из лабы 4
|
||
(плюс небольшие расходы на обмен).
|
||
- На настоящих нескольких машинах код не меняется — меняется только
|
||
запуск (это главный смысл MPI).
|
||
|
||
### Измерения для отчёта
|
||
|
||
```bat
|
||
mpiexec -n 1 python lab08_mpi\solution.py
|
||
mpiexec -n 2 python lab08_mpi\solution.py
|
||
mpiexec -n 4 python lab08_mpi\solution.py
|
||
```
|
||
|
||
## Контрольные вопросы
|
||
|
||
1. Чем модель передачи сообщений отличается от модели общей памяти?
|
||
2. Что делает `scatter` и чем она отличается от `bcast`?
|
||
3. Почему в MPI функции не нужно пиккелить, а в multiprocessing — нужно?
|
||
4. Что такое ранг? Как ранг 0 узнаёт, сколько всего рангов?
|
||
5. Когда MPI выгоднее multiprocessing, даже на одном компьютере?
|
||
|
||
## Что сдаётся
|
||
|
||
`solution.py` + отчёт: вывод при `-n 1/2/4`, таблица T(p), сравнение с
|
||
лабой 4, ответы на вопросы.
|
||
|
||
## Критерии оценки
|
||
|
||
| Пункт | Баллы |
|
||
|---|---|
|
||
| mpi_run: checksum корректен при -n 1 и -n 4 | 3 |
|
||
| mpi_pi через reduce | 2 |
|
||
| Замеры при разном числе рангов | 2 |
|
||
| Отчёт и ответы на вопросы | 3 |
|
||
|
||
## Типичные ошибки
|
||
|
||
- Код с веткой «если rank == 0» выполняется ВСЕМИ рангами — не заворачивайте
|
||
всю программу в `if rank == 0`.
|
||
- Разные random на разных рангах без фиксации зерна — невоспроизводимый
|
||
результат.
|
||
- Заблокированный `recv` без парной `send` — программа висит. Начинайте с
|
||
коллективных операций, они безопаснее. |