Память, мультипроцессоры, кластеры и GRID
Две фундаментальные модели памяти
Любую параллельную систему можно описать через то, как процессоры видят память:
Главный вопрос для новичка: «Могу ли я, как программист, обратиться к переменной X с другого ядра так же, как с текущего?»
| Ответ | Модель | Как обычно программируют |
|---|---|---|
| Да, одна куча на всех | Shared memory | Потоки, OpenMP, std::thread |
| Нет, у каждого свой RAM | Distributed memory | MPI, отдельные процессы, Send/Recv |
Общая память (Shared Memory)
Все процессоры обращаются к одному адресному пространству. Процессор P₁ пишет в ячейку X, процессор P₂ читает X — оба видят одни и те же данные (с учётом когерентности кэша).
┌─────────┐ ┌─────────┐
│ CPU 0 │ │ CPU 1 │
└────┬────┘ └────┬────┘
│ общая │
└───────┬───────┘
▼
[ RAM / шина ]
Плюсы: простой обмен через переменные; хорошо ложится на многопоточность.
Минусы: при росте числа процессоров шина памяти становится узким местом; нужна синхронизация (мьютексы, атомики) — см. потоки.
Примеры: односокетный многоядерный PC, сервер с SMP, NUMA-система (память «ближе» к одному сокету).
Распределённая память (Distributed Memory)
У каждого процессора своя локальная память. Узел A не может просто прочитать адрес узла B — только через явный обмен (сообщения, сеть).
Аналогия: у каждого сотрудника свой шкаф с документами. Чтобы коллега увидел ваш файл, вы отправляете копию (сообщение), а не указываете «возьми из моей памяти по адресу 0x7fff…». В MPI каждый rank — отдельный процесс со своим адресным пространством; общие данные — только через MPI_Send, MPI_Recv, MPI_Bcast.
┌──────────┐ сеть ┌──────────┐
│ CPU + RAM│◄──────►│ CPU + RAM│
└──────────┘ └──────────┘
Плюсы: хорошо масштабируется на тысячи узлов; нет единой точки contention за память.
Минусы: программист (или библиотека) явно отправляет и принимает данные; ошибки в протоколе обмена.
Примеры: кластеры HPC, MPI-программы, распределённое обучение на нескольких серверах.
Мультипроцессор и мультикомпьютер
| Мультипроцессор | Мультикомпьютер | |
|---|---|---|
| Память | Общая (логически) | Распределённая |
| Связь | Шина, crossbar, NUMA | Сеть (Ethernet, InfiniBand) |
| Масштаб | Обычно 1–8 сокетов, до сотен ядер | От десятков до миллионов ядер |
| Программирование | OpenMP, pthreads | MPI, PGAS |
| Типичный пример | Workstation, сервер | Кластер / суперкомпьютер |
NUMA (Non-Uniform Memory Access) — промежуточный случай: память общая, но скорость доступа зависит от того, «свой» это узел или «чужой». Без NUMA-aware размещения потоков и данных производительность падает на 30–50 %.
UMA и NUMA
- UMA (Uniform Memory Access) — все ядра видят RAM с одинаковой задержкой (типичный односокетный PC).
- NUMA — несколько узлов памяти, каждый привязан к группе ядер. Локальная память быстрее удалённой в 1.5–3 раза.
Рекомендации для NUMA:
- Привязать поток к ядру (
taskset, OpenMPproc_bind). - Выделять данные на том же узле, где они обрабатываются (
numactl --membind). - Минимизировать false sharing — когда разные ядра пишут в разные переменные, но в одной кэш-линии.
Кластеры вычислений
Кластер — группа связанных узлов (нод), работающих как единая вычислительная система. В HPC это чаще Beowulf-класс: commodity-серверы + быстрая сеть + общее ПО (Linux, Slurm, MPI).
Компоненты:
| Компонент | Роль |
|---|---|
| Узлы вычисления | Запускают задачи |
| Head / login node | Вход пользователя, сборка, отправка job |
| Сеть | InfiniBand, Omni-Path, иногда 100 Gb Ethernet |
| Планировщик | Slurm, PBS, LSF — очередь задач |
| Общее хранилище | NFS, Lustre, GPFS — большие данные |
Суперкомпьютер — по сути очень большой tightly-coupled кластер с топовой сетью и охлаждением.
Kubernetes-кластер в DevOps — тоже кластер, но цель другая (оркестрация контейнеров), не линейная speedup одной численной задачи. Для HPC иногда используют K8s + MPI, но классика — bare metal + Slurm.
GRID и метакомпьютинг
GRID (вычислительная сетка) — объединение разнородных ресурсов (университеты, лаборатории, добровольцы) в единую инфраструктуру для крупных задач. Отличие от кластера:
| Кластер | GRID | |
|---|---|---|
| Администрирование | Один центр | Федерация организаций |
| Однородность | Высокая | Низкая (разное железо, ОС) |
| Связь | Низкая latency | Часто интернет, высокая latency |
| Пример | Суперкомпьютер в МГУ | LHC Computing Grid, BOINC |
Метакомпьютинг — уровень над отдельными системами: пользователь отправляет задачу «в сеть», а middleware (Globus, Univa Grid Engine) выбирает, где её выполнить, переносит данные и собирает результат. Программист часто не знает физический узел.
Примеры метакомпьютинга «для всех»:
- SETI@home, Folding@home — добровольные вычисления на домашних ПК.
- Облачный burst — докупить 500 VM на время расчёта.
Отображение задачи на систему (проблема mapping)
Проблема отображения — как логические процессы алгоритма (P₀…P_n−1) разместить на физических процессорах и минимизировать время:
- коммуникаций (на distributed);
- contention за память (на shared);
- простоя (load imbalance).
Это NP-трудная задача в общем виде; на практике используют эвристики: блоки матрицы → соседние ядра; 1 MPI-процесс на NUMA-узел; static chunk size в OpenMP.
Формальные модели отображения связаны с топологиями сетей и графом алгоритма.
Программные интерфейсы под модель памяти
| Модель | API | Когда выбирать |
|---|---|---|
| Shared, 1 машина | OpenMP, TBB, std::thread | До ~64 ядер, один узел |
| Shared + GPU | CUDA, OpenMP target, SYCL | Массовый data-parallel |
| Distributed | MPI | Кластер, несколько узлов |
| Гибрид | MPI + OpenMP | Крупнейшие суперкомпьютеры: MPI между узлами, OpenMP внутри |
Справочник по OpenMP/MPI в контексте Fortran: 811.
Когерентность кэша (MESI — обзор)
В shared memory у каждого ядра свой L1/L2. Как все видят одну переменную X?
Протокол MESI (Modified, Exclusive, Shared, Invalid) — строка кэша в одном из состояний:
| Состояние | Смысл |
|---|---|
| M | Только это ядро изменило строку; память устарела |
| E | Только это ядро держит копию, не менял |
| S | Несколько ядер читают одну строку |
| I | Копия недействительна |
Запись другим ядром → invalidation → cache miss и повторная загрузка. Отсюда false sharing: два потока пишут в разные переменные в одной cache line (64 B) → постоянные invalidations.
Практика: padding структур, локальные буферы, reduction вместо atomic++ на каждый элемент.
First-touch на NUMA
numactl --hardware # узлы NUMA
export OMP_PROC_BIND=close
First-touch: страница RAM выделяется на NUMA-узле того ядра, которое первым записало в неё.
#pragma omp parallel for
for (int i = 0; i < n; ++i)
a[i] = 0; // каждый поток «touch» свой диапазон → локальная память
Если инициализировать массив одним потоком, а считать всеми — данные на «чужом» узле, минус 30–50 % скорости.
Сеть кластера — порядок величин
| Технология | Latency (тип.) | Bandwidth | Комментарий |
|---|---|---|---|
| Ethernet 1 Gb | ~100 µs | ~100 MB/s | Учебный кластер |
| 100 Gb Ethernet | ~10 µs | ~10 GB/s | HPC entry |
| InfiniBand HDR | ~1 µs | ~200 GB/s | Топовые суперкомпьютеры |
| NVLink (GPU) | sub-µs | сотни GB/s | Внутри узла |
Правило: одно сообщение ≥ нескольких KB (лучше MB), иначе доминирует latency, а не bandwidth — см. MPI-практику.
Гибрид — узел как SMP, кластер как distributed
Современный HPC-узел — 32–128 ядер, shared/NUMA память. Кластер из N таких узлов:
[ Node 0: OpenMP × 64 ] ←──MPI──→ [ Node 1: OpenMP × 64 ] …
- Внутри узла — shared memory, halo через shared buffer или OpenMP.
- Между узлами — MPI, только необходимые блоки.
Так минимизируют число MPI-процессов и не дублируют всю память на каждый rank.
Что дальше
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Введение в параллельные вычисления — зачем они нужны, чем отличаются от асинхронности, основные проблемы высокопроизводительных вычислений (HPC). Сети Петри для моделирования параллельных процессов, диаграммы расписания, связь с графом алгоритма. Практическое параллельное программирование — OpenMP, MPI, типовые паттерны, профилирование и отладка параллельного кода. Классификация параллельных архитектур — таксономия Флинна, SIMD и MIMD, векторно-конвейерные системы, степень достижимого параллелизма. Модели параллельных вычислений — PRAM, message passing, SPMD; сети передачи данных между процессорами; диаграммы расписания. Граф алгоритма — построение, свойства, матрица следования, выявление логически несовместимых операторов и параллелизма. Временные характеристики параллельных алгоритмов — информационный граф, ранние и поздние сроки, критический путь, минимальное число процессоров. Оценка производительности параллельных компьютеров — закон Амдала, закон Густафсона-Барсиса, эффективность, масштабируемость, конвейер. Построение параллельных алгоритмов — инженерный подход, классификация параллелизма, этапы разработки, декомпозиция данных, рекомендации. Параллельные алгоритмы умножения матриц — последовательная база, блочная декомпозиция, Cannon, SUMMA, практические рекомендации. Краткие итоги раздела «Параллельные вычисления». Вопросы для самопроверки по разделу «Параллельные вычисления».Параллельные вычислительные процессы — введение
Сети Петри и формальные расписания
Практика — OpenMP, MPI и профилирование
Классификация параллельных архитектур
Модели параллельных вычислений и топологии
Граф алгоритма и матрица следования
Временной анализ параллельных алгоритмов
Законы производительности параллельных систем
Инженерия параллельных алгоритмов
Параллельное умножение матриц
Итоги
Чек-лист самопроверки