Лекция 10:
Стандарт MPI
(MPI – Message Passing Interface)
Курносов Михаил Георгиевич
к.т.н. доцент Кафедры вычислительных систем
Сибирский государственный университет
телекоммуникаций и информатики
http://www.mkurnosov.net
Стандарт MPI
 Message Passing Interface (MPI) – это стандарт
на программный интерфейс коммуникационных
библиотек для создания параллельных программ
в модели передачи сообщений (message passing)

 Стандарт определяет интерфейс для языков
программирования C и Fortran

MPI 1.0

MPI 1.1

MPI 1.2, 2.0

MPI 1.3, 2.1

MPI 2.2

MPI 3.0

1994

1995

1997

2008

2009

2012

http://www.mpi-forum.org
http://www.mpi-forum.org/docs/mpi-3.0/mpi30-report.pdf

2
Реализации стандарт MPI (библиотеки)
 MPICH2 (Open source, Argone NL)
http://www.mcs.anl.gov/research/projects/mpich2

 MVAPICH2
 IBM MPI
 Cray MPI
 Intel MPI
 HP MPI
 SiCortex MPI
 Open MPI (Open source, BSD License)
http://www.open-mpi.org

 Oracle MPI
3
Различия в реализациях стандарта MPI
 Спектр поддерживаемых архитектур процессоров:
Intel, IBM, ARM, Fujitsu, NVIDIA, AMD
 Типы поддерживаемых коммуникационных технологий/сетей:
InfiniBand, 10 Gigabit Ethernet, Cray Gemeni, IBM PERCS/5D torus,
Fujitsu Tofu, Myrinet, SCI
 Реализованные протоколы дифференцированных обменов
(Point-to-point): хранение списка процессов, подтверждение
передачи (ACK), буферизация сообщений, …
 Коллективные операции обменов информацией:
коммуникационная сложность алгоритмов, учет структуры
вычислительной системы (torus, fat tree, …), неблокирующие
коллективные обмены (MPI 3.0, методы хранение collective schedule)
 Алгоритмы вложения графов программ в структуры
вычислительных систем (MPI topology mapping)
 Возможность выполнения MPI-функций в многопоточной среде
и поддержка ускорителей (GPU NVIDIA/AMD, Intel Xeon Phi)

4
Стандарт MPI
Сеть InfiniBand (вычислительная сеть)

Общая память

Ядро

Ядро

Процессор
Ядро

Процессор

Ядро

Ядро

Compute node 1

Процесс 0

Frontend (login node)

Процессор

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 2

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 3

Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …)

Процессор
Ядро

Ядро

Процессор
Ядро

Ядро

Compute node 4

Процесс 2

Процессор

Общая память

Процесс 3

Общая память

Ядро

Процесс 5

Процессор

Ядро

Процесс 4

Ядро

Процесс 1

Ядро

MPI_COMM_WORLD

 Коммуникатор (communicator) – множество процессов,
образующих логическую область для выполнения
коллективных операций (обменов информацией и др.)
 В рамках коммуникатора ветви имеют номера: 0, 1, …, n – 1

5
MPI: Hello World
#include <mpi.h>
int main(int argc, char *argv[])
{
int rank, commsize;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &commsize);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf(“Hello, World: process %d of %dn”,
rank, commsize);
MPI_Finalize();
return 0;
}
6
Компиляция MPI-программ
Компиляция C-программы:
$ mpicc –o prog ./prog.c
Компиляция C++-программы:
$ mpicxx –o prog ./prog.cpp
Компиляция Fortran-программы:

$ mpif90 –o prog ./prog.f
Компиляция C-программы компилятором Intel C/C++:

$ mpicc –cc=icc –o prog ./prog.c
7
Запуск MPI-программ на кластере Jet
1. Формируем job-файл с ресурсным запросом (task.job):
#PBS –N MyTask
#PBS -l nodes=4:ppn=1

# 4 узла, 1 процесс на узел

#PBS -j oe
cd $PBS_O_WORKDIR
mpiexec ./prog

2. Ставим задачу в очередь системы пакетной обработки
заданий TORQUE
$ qsub task.job
7719.jet

Job ID

8
Выполнение MPI-программ
Сеть InfiniBand (вычислительная сеть)

Ядро

Процессор

Ядро

Общая память

Ядро

Процессор

Общая память

Frontend (login node)

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 1

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 2

Процессор
Ядро

Общая память

Процессор

Ядро

Ядро

Ядро

Compute node 3

Процессор
Ядро

Ядро

Процесс 2

Процесс 1

Ядро

Ядро

Compute node 4

Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …)

Процесс 0

Процессор

Процесс 3

Ядро

MPI_COMM_WORLD

 Система TORQUE запустит задачу, когда для нее будут
доступны ресурсы
 В данном примере на каждом узле могут также присутствовать
3 процесса других задач (пользователей)

9
Состояние заданий в очереди TORQUE
$ qstat
Job id

Name

User

Time Use S Queue

------------- ---------------- --------------- -------- - -----

7732.jet

simmc

mkurnosov

00:00:00 C debug

7733.jet

Heat2D

mkurnosov

0 R debug

7734.jet

simmc

mkurnosov

00:00:00 C debug

7735.jet

mpitask

mkurnosov

00:00:00 C debug

7736.jet

simmc

mkurnosov

00:00:00 C debug

7737.jet

mpitask

mkurnosov

00:00:00 C debug

7738.jet

Heat2D

mkurnosov

0 R debug

Job state
 C – completed
 R – running
 Q – waiting in queue

10
Формирование ресурсных запросов (Job)
 8 ветвей – все ветви на одном узле
#PBS -l nodes=1:ppn=8
 6 ветвей – две ветви на каждом узле
#PBS -l nodes=3:ppn=2

 Команды управления задачами в очереди
o qdel – удаление задачи из очереди
o qstat – состояние очереди
o pbsnodes – состояние вычислительных узлов
11
Стандарт MPI
 Двусторонние обмены (Point-to-point communication)
 Составные типы данных (Derived datatypes)
 Коллективные обмены (Collective communication)
 Группы процессов, коммуникаторы
(Groups, communicators)
 Виртуальные топологии процессов
 Управление средой выполнения
(Environmental management)
 Управление процессами (Process management)

 Односторонние обмены (One-sided communication)
 Файловый ввод-вывод (I/O)
12
Двусторонние обмены (Point-to-point)
 Двусторонний обмен (Point-to-point communication) –
это обмен сообщением между двумя процессами:
один инициирует передачу (send), а другой – получение
сообщения (receive)
 Каждое сообщение снабжается конвертом (envelope):
− номер процесса отправителя (source)
− номер процесса получателя (destination)

− тег сообщения (tag)
− коммуникатор (communicator)
13
Двусторонние обмены (Point-to-point)
Блокирующие (Blocking)









MPI_Bsend
MPI_Recv
MPI_Rsend
MPI_Send
MPI_Sendrecv
MPI_Sendrecv_replace
MPI_Ssend
...

Проверки состояния запросов
(Completion/Testing)






MPI_Iprobe
MPI_Probe
MPI_Test{, all, any, some}
MPI_Wait{, all, any, some}
...

Неблокирующие
(Non-blocking)







MPI_Ibsend
MPI_Irecv
MPI_Irsend
MPI_Isend
MPI_Issend
...

Постоянные (Persistent)







MPI_Bsend_init
MPI_Recv_init
MPI_Send_init
...
MPI_Start
MPI_Startall

14
MPI_Send/MPI_Recv
int MPI_Send(void *buf,
int count,
MPI_Datatype datatype,
int dest,
int tag,
MPI_Comm comm)






buf – указатель на буфер с информацией
count – количество передаваемых элементов типа datatype
dest – номер процесса получателя сообщения
tag – тег сообщения

int MPI_Recv(void *buf, int count,
MPI_Datatype datatype,
int source, int tag,
MPI_Comm comm, MPI_Status *status)

15
Пример MPI_Send/MPI_Recv
int main(int argc, char **argv)
{
int rank;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0)
MPI_Send(buf, BUFSIZE, MPI_DOUBLE, 1, 1,
MPI_COMM_WORLD);
else
MPI_Recv(buf, BUFSIZE, MPI_DOUBLE, 0, 1,
MPI_COMM_WORLD, &status);

MPI_Finalize();
return 0;
}

16
Пример MPI_Send/MPI_Recv
int main(int argc, char **argv)
{
int rank;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

Infinite waiting

if (rank == 0)
MPI_Send(buf, BUFSIZE, MPI_DOUBLE, 1, 1,
(if commsize > 2)
MPI_COMM_WORLD);
else
MPI_Recv(buf, BUFSIZE, MPI_DOUBLE, 0, 1,
MPI_COMM_WORLD, &status);

MPI_Finalize();
return 0;
}

17
Бесконечное ожидание (commsize = 3)
Сеть InfiniBand (вычислительная сеть)
Общая память

Ядро

Процессор

Общая память

Frontend (login node)

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 1

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 2

MPI_Send

MPI_Recv

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 3

Процессор
Ядро

Ядро

Процессор
Ядро

Ядро

Compute node 4

Процесс 2

Процессор

Ядро

Процесс 1

Ядро

Процесс 0

Ядро

Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …)

MPI_Recv

∞
waiting for
a message

Нет парного
MPI_Send

18
Пример MPI_Send/MPI_Recv
int main(int argc, char **argv)
{
int rank;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0)
MPI_Send(buf, BUFSIZE, MPI_DOUBLE, 1, 1,
MPI_COMM_WORLD);
else if (rank == 1)
MPI_Recv(buf, BUFSIZE, MPI_DOUBLE, 0, 1,
MPI_COMM_WORLD, &status);

MPI_Finalize();
return 0;
}

19
Режимы передачи сообщений
 Standard (MPI_Send) – библиотека сама принимает
решение буферизовать сообщение и сразу выходить
из функции или дожидаться окончания передачи данных
получателю (например, если нет свободного буфера)
 Buffered (MPI_Bsend) – отправляемое сообщение
помещается в буфер и функция завершает свою работу.
Сообщение будет отправлено библиотекой.
 Synchronous (MPI_Ssend) – функция завершает своё
выполнение когда процесс-получатель вызвал MPI_Recv
и начал копировать сообщение. После вызова функции
можно безопасно использовать буфер.
 Ready (MPI_Rsend) – функция успешно выполняется только
если процесс-получатель уже вызвал функцию MPI_Recv
20
Неблокирующие двусторонние обмены
 Неблокирующие передача/прием сообщений
(Nonblocking point-to-point communication) –
операция, выход из которой осуществляется не дожидаясь
завершения передачи информации
 Пользователю возвращается дескриптор запроса (request),
который он может использовать для проверки состояния
операции
 Цель – обеспечить возможность совмещения вычислений
и обменов информацией (Overlap computations &
communications)
int MPI_Isend(const void *buf, int count,
MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm,
MPI_Request *request)

21
Network offload
Высокопроизводительные сетевые контроллеры аппаратурно реализуют
часть функций сетевого стека => низкая латентность передачи
сообщений, совмещение обменов и вычислений

MPI
TCP/UDP
IP
MAC
PHY

MPI

Операционная
система

Сетевой
интерфейс

Операционная система

TCP/UDP
IP

MAC

TOE
сетевой
интерфейс

PHY
22
InfiniBand aware MPI

http://www.mn.uio.no/astro/english/services/it/help/programming/mpi.html

23
Неблокирующие двусторонние обмены
 int MPI_Wait(MPI_Request *request,
MPI_Status *status)

Блокирует выполнение процесса пока не закончится
операция, ассоциированная с запросом request
 int MPI_Test(MPI_Request *request, int *flag,
MPI_Status *status)

Возвращает flag = 1 если операция, ассоциированная
с запросом request, завершена

24
Совмещение обменов и вычислений
MPI_Isend(buf, count, MPI_INT, 1, 0,
MPI_COMM_WORLD, &req);
do {
//
// Вычисления ... (не использовать buf)
//
MPI_Test(&req, &flag, &status);
} while (!flag)

25
Неблокирующие двусторонние обмены
 int MPI_Waitany(int count,
MPI_Request array_of_requests[],
int *index, MPI_Status *status)

 int MPI_Waitall(int count,
MPI_Request array_of_requests[],
MPI_Status array_of_statuses[])
 int MPI_Testany(int count,
MPI_Request array_of_requests[],
int *index, int *flag,
MPI_Status *status)
 int MPI_Testall(int count,
MPI_Request array_of_requests[],
int *flag,
MPI_Status array_of_statuses[])
26
Типы данных MPI (Datatypes)













MPI_PACKED 1
MPI_BYTE 1
MPI_CHAR 1
MPI_UNSIGNED_CHAR 1
MPI_SIGNED_CHAR 1
MPI_WCHAR 2
MPI_SHORT 2
MPI_UNSIGNED_SHORT 2
MPI_INT 4
MPI_UNSIGNED 4
MPI_LONG 4
MPI_UNSIGNED_LONG 4












MPI_FLOAT 4
MPI_DOUBLE 8
MPI_LONG_DOUBLE 16
MPI_CHARACTER 1
MPI_LOGICAL 4
MPI_INTEGER 4
MPI_REAL 4
MPI_DOUBLE_PRECISION 8
MPI_COMPLEX 2*4
MPI_DOUBLE_COMPLEX 2*8

В MPI имеется возможность создавать пользовательские
типы данных (Derived Datatypes, структуры, части массивов)

27
Коллективные обмены (блокирующие)
Трансляционный обмен (One-to-all)
o MPI_Bcast
o MPI_Scatter
o MPI_Scatterv
Коллекторный обмен (All-to-one)
o MPI_Gather
o MPI_Gatherv
o MPI_Reduce
Трансляционно-циклический обмен (All-to-all)
o MPI_Allgather
o MPI_Allgatherv
o MPI_Alltoall
o MPI_Alltoallv
o MPI_Allreduce
o MPI_Reduce_scatter

28
MPI_Bcast (One-to-all broadcast)
int MPI_Bcast(void *buf, int count,
MPI_Datatype datatype,
int root, MPI_Comm comm)

Элементы

A

A

MPI_Bcast
Рассылка всем
одинакового сообщения

Процессы

Процессы

Элементы

A
A
A

 MPI_Bcast – рассылка всем процессам сообщения buf
 Если номер процесса совпадает с root, то он отправитель,
иначе – приемник
29
MPI_Bcast (One-to-all broadcast)
int MPI_Bcast(void *buf, int count,
MPI_Datatype datatype,
int root, MPI_Comm comm)
int main(int argc, char *argv[])
{
double buf[n];
MPI_Init(&argc,&argv);
/* Code */
// Передать массив buf всем процессам (и себе)
MPI_Bcast(&buf, n, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}

30
MPI_Scatter (One-to-all broadcast)
int MPI_Scatter(void *sendbuf, int sendcnt,
MPI_Datatype sendtype,
void *recvbuf, int recvcnt,
MPI_Datatype recvtype,
int root, MPI_Comm comm)

A0

A1

A2

Элементы
A3

MPI_Scatter
Рассылка всем
разных сообщений

Процессы

Процессы

Элементы

A0
A1
A2
A3

 Размер sendbuf = sizeof(sendtype) * sendcnt * commsize
 Размер recvbuf = sizeof(sendtype) * recvcnt
31
MPI_Gather (All-to-one)
int MPI_Gather(void *sendbuf, int sendcnt,
MPI_Datatype sendtype,
void *recvbuf, int recvcount,
MPI_Datatype recvtype,
int root, MPI_Comm comm)

A0

A1

A2

Элементы
A3

MPI_Gather
Прием от всех
разных сообщений

Процессы

Процессы

Элементы

A0
A1
A2
A3

 Размер sendbuf: sizeof(sendtype) * sendcnt
 Размер recvbuf: sizeof(sendtype) * sendcnt * commsize

32
MPI_Reduce (All-to-one)
int MPI_Reduce(void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root,
MPI_Comm comm)
Rank 0 (root)

Rank 1

Rank 2

1 2 3 1 7

4 5 6 1 3

7 8 9 9 3

+

+

+

12 15 18 11 13
recvbuf

Формирование в процессе root массива
значений, полученных применением
операции op

 Размер sendbuf: sizeof(datatype) * count
 Размер recvbuf: sizeof(datatype) * count

33
Операции MPI_Reduce







MPI_MAX
MPI_MIN
MPI_MAXLOC
MPI_MINLOC
MPI_SUM
MPI_PROD








MPI_LAND
MPI_LOR
MPI_LXOR
MPI_BAND
MPI_BOR
MPI_BXOR

int MPI_Op_create(MPI_User_function *function,
int commute, MPI_Op *op)

 Операция пользователя должна быть ассоциативной
A * (B * C) = (A * B) * C
 Если commute = 1, то операция коммутативная
A*B=B*A

34
Пользовательская операция MPI_Reduce
typedef struct {
double real, imag;
} Complex;
Complex sbuf[100], rbuf[100];
MPI_Op complexmulop;
MPI_Datatype ctype;
MPI_Type_contiguous(2, MPI_DOUBLE, &ctype);
MPI_Type_commit(&ctype);

// Умножение комплексных чисел
MPI_Op_create(complex_mul, 1, &complexmulop);
MPI_Reduce(sbuf, rbuf, 100, ctype, complexmulop,
root, comm);

MPI_Op_free(&complexmulop);
MPI_Type_free(&ctype);
35
Пользовательская операция MPI_Reduce
// Умножение массивов комплексных чисел
void complex_mul(void *inv, void *inoutv, int *len,
MPI_Datatype *datatype)
{
int i;
Complex c;
Complex *in = (Complex *)inv;
*inout = (Complex *)inoutv;
for (i = 0; i < *len; i++)
c.real = inout->real *
inout->imag *
c.imag = inout->real *
inout->imag *
*inout = c;
in++;
inout++;
}

{
in->real in->imag;
in->imag +
in->real;

}

36
MPI_Alltoall
int MPI_Alltoall(void *sendbuf, int sendcount,
MPI_Datatype sendtype,
void *recvbuf, int recvcnt,
MPI_Datatype recvtype, MPI_Comm comm)

Элементы

A0

A1

A2

A3

B0

B1

B2

B3

C0

C1

C2

C3

D0

D1

D2

D3

MPI_Alltoall
В каждом процессе
собираются сообщения
всех процессов

Процессы

Процессы

Элементы

A0

B0

C0

D0

A1

B1

C1

D1

A2

B2

C2

D2

A3

B3

C3

D3

 Размер sendbuf: sizeof(sendtype) * sendcount * commsize
 Размер recvbuf: sizeof(recvtype) * recvcount * commsize

37
MPI_Barrier
int MPI_Barrier(MPI_Comm comm)
 Барьерная синхронизация процессов – ни один процесс
не завершит выполнение этой функции пока все не
войдут в неё
int main(int argc, char *argv[])
{
/* Code */
MPI_Barrier(MPI_COMM_WORLD);
/* Code */
MPI_Finalize();
return 0;
}

38
All-to-all
int MPI_Allgather(void *sendbuf, int sendcount,
MPI_Datatype sendtype,
void *recvbuf, int recvcount,
MPI_Datatype recvtype,
MPI_Comm comm)
int MPI_Allgatherv(void *sendbuf, int sendcount,
MPI_Datatype sendtype,
void *recvbuf, int *recvcounts,
int *displs,
MPI_Datatype recvtype,
MPI_Comm comm)
int MPI_Allreduce(void *sendbuf, void *recvbuf,
int count, MPI_Datatype datatype,
MPI_Op op, MPI_Comm comm)

39
Реализация коллективных операций
 Аппаратная реализация коллективных операций –
специализированные коммуникационные сети:
IBM BlueGene/P Tree network, barrier network
 Программная реализация – коллективная операция
реализуется на основе операций Send/Recv
 Программная реализация коллективных операций имеет
наибольшее распространение (MPICH2, Open MPI)

40
Алгоритмы коллективных операций
One-to-all Broadcast (MPI_Bcast, MPI_Scatter)

 Алгоритм биномиального дерева (Binomial tree)
 Scatter + Allgather
All-to-one Broadcast (MPI_Reduce, MPI_Gather):

 Алгоритм биномиального дерева
 Reduce_Scatter + Gather

а0 + a1

0

MPI_Recv

а0 + a1 + а2 + a3
MPI_Recv

а0 + … + a5
MPI_Recv

a1

All-to-all Broadcast
(MPI_Allgather, MPI_Allreduce)

1
2

MPI_Send
MPI_Recv

а2 + a3

MPI_Send

a4 + а5

a3





Алгоритм рекурсивного
сдваивания (Recursive doubling)

Алгоритм Дж. Брука
(J. Bruck, 1997)

3
4

MPI_Send
MPI_Recv

MPI_Send

a5

5

MPI_Send

t

Диаграмма выполнения операции MPI_Reduce
алгоритмом биномиального дерева
(суммирование элементов 6 ветвей)

41
Реализация коллективных операций
 Как правило, для каждой коллективной операции имеется несколько
алгоритмов ее реализации – выбор алгоритма основан на эвристике
int MPIR_Allgather_intra(...)
mpich2-3.0.4
{
// ...
tot_bytes = (MPI_Aint)recvcount * comm_size * type_size;
if ((tot_bytes < MPIR_PARAM_ALLGATHER_LONG_MSG_SIZE) &&
!(comm_size & (comm_size - 1))) {
/* Short or medium size message and power-of-two no.
of processes. Use recursive doubling algorithm */
// ...
else if (tot_bytes < MPIR_PARAM_ALLGATHER_SHORT_MSG_SIZE) {
/* Short message and non-power-of-two no. of processes.
Use Bruck algorithm (see description above). */
// ...
else {
/* Long message or medium-size message and non-power-of-two
no. of processes. Use ring algorithm. */
// ...
}
}

42
Выбор алгоритма коллективной операции
 Как динамически (во время вызова функции)
выбирать алгоритм реализации коллективной
операции?
 Учитывать только размер сообщения?
Такой выбор будет оптимальным только для той системы,
на которой провели предварительные замеры времени
выполнения операций (MPICH2, Open MPI)
 Использовать данные предварительных замеров
времени выполнения алгоритмов (при установке
библиотеки на вычислительную систему)?
 Оценивать время выполнения алгоритма на модели
(LogP, LogGP, PLogP, …)?

43
Алгоритмы реализации MPI_Allgather
Кольцевой алгоритм (ring)
a0 a1 a2 a3

0

a0 a1 a2 a3

3

1

a0 a1 a2 a3

2
a0 a1 a2 a3

Каждая ветвь выполняет 2(n – 1) обменов (Send/Recv)
44
Алгоритмы реализации MPI_Allgather
Алгоритм рекурсивного
сдваивания (recursive doubling)

Алгоритм Дж. Брука
(J. Bruck et al., 1997)

0

1

2

3

4

5

6

7

0

1

2

3

4

5

6

7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a0
a1
a2
a3
a4
a5
a6
a7

a1
0
a2
1
a3
2
a4
3
a5
4
a6
5
a7
6
a0
7

a2
0
a3
1
a4
2
a5
3
a6
4
a7
5
a0
6
a1
7

a3
0
a4
1
a5
2
a6
3
a7
4
a0
5
a1
6
a2
7

a4
0
a5
1
a6
2
a7
3
a0
4
a1
5
a2
6
a3
7

a5
0
a6
1
a7
2
a0
3
a1
4
a2
5
a3
6
a4
7

a6
0
a7
1
a0
2
a1
3
a2
4
a3
5
a4
6
a5
7

a7
0
a0
1
a1
2
a2
3
a3
4
a4
5
a5
6
a6
7

Количество обменов: 2log2n

Только для n равного степени двойки

Количество обменов: 2log2n
На шаге k ветвь i взаимодействует
с ветвями (i – 2k + n) mod n
и (i + 2k) mod n

На каждом шаге размер передаваемого блока удваивается: m, 2m, 4m

45
Таймер
double MPI_Wtime()

int main(int argc, char **argv)
{
double t = 0.0
t -= MPI_Wtime();
/* Code */
t += MPI_Wtime();
printf(“Elapsed time: %.6f secondsn", t);
}

46
Неблокирующие коллективные операции
MPI 3.0
 Неблокирующий коллективный обмен
(Non-blocking collective communication) –
коллективная операция, выход из которой осуществляется
не дожидаясь завершения операций обменов

 Пользователю возвращается дескриптор запроса (request),
который он может использовать для проверки состояния
операции
 Цель – обеспечить возможность совмещения вычислений
и обменов информацией
int MPI_Ibcast(void *buf, int count,
MPI_Datatype datatype,
int root, MPI_Comm comm,
MPI_Request *request)
47
MPI_Ibcast

MPI 3.0

MPI_Request req;
MPI_Ibcast(buf, count, MPI_INT, 0,
MPI_COMM_WORLD, &req);
while (!flag) {
// Вычисления...
// Проверяем состояние операции
MPI_Test(&req, &flag, MPI_STATUS_IGNORE);

}
MPI_Wait(&req, MPI_STATUS_IGNORE);
48
Non-blocking MPI Collectives

MPI 3.0

Блокирующие коллективные операции
(Blocking MPI Collectives)
MPI_Bcast

MPI lib.

Waiting

User code

Computations

t

tblocking
Неблокирующие коллективные операции
(Non-blocking collectives)
MPI_Wait

MPI_Ibcast

MPI lib.

Computations

User code

tinit

tcomp
toverlap

t

twait
49
Non-blocking MPI Collectives

MPI 3.0

Блокирующие коллективные операции
Коэффициент 𝜶 совмещения вычисления
(Blocking MPI Collectives)
MPI lib.

и обменов информацией
MPI_Bcast
𝒕 𝒐𝒗𝒆𝒓𝒍𝒂𝒑 Computations
− 𝒕 𝒄𝒐𝒎𝒑
𝜶= 𝟏−
tblocking
𝒕 𝒃𝒍𝒐𝒄𝒌𝒊𝒏𝒈
Waiting

User code

t

Неблокирующие коллективные операции
(Non-blocking collectives)
MPI_Wait

MPI_Ibcast

MPI lib.

Computations

User code

tinit

tcomp
toverlap

t

twait
50
Неблокирующие коллективные операции
 При вызове неблокирующией коллективной операции
создается расписание выполнения обменов
(collective schedule)
 Progress engine – механизм, который в фоновом режиме
реализует обмены по созданному расписанию – как правило
обмены выполняются при вызове MPI_Test (в противном
случае необходим дополнительный поток)
MPI_Request req;
MPI_Ibcast(buf, count, MPI_INT, 0, MPI_COMM_WORLD, &req);
while (!flag) {
// Вычисления...
// Проверяем состояние и продвигаем обмены по расписанию
MPI_Test(&req, &flag, MPI_STATUS_IGNORE);
}
MPI_Wait(&req, MPI_STATUS_IGNORE);

51
Вычисление числа π
1

𝜋=
0

4
𝑑𝑥
2
1+ 𝑥

𝑛

𝜋≈ℎ
𝑖=1

4
1 + (ℎ(𝑖 − 0.5))2
1
ℎ=
𝑛

4,50

4,00
3,50
3,00
2,50
2,00
1,50
1,00

0,50
0,00
0,00 0,10 0,20 0,30 0,40 0,50 0,60 0,70 0,80 0,90

52
Вычисление числа π (sequential version)
int main()
{
int i, nsteps = 1000000;
double step = 1.0 / (double)nsteps;
double pi, x, sum = 0.0;
for (i = 1; i <= nsteps; i++) {
x = step * ((double)i - 0.5);
sum += 4.0 / (1.0 + x * x);
}
pi = step * sum;
printf(“PI = %.16fn”, pi);
return 0;
}

53
Вычисление числа π (parallel version)
int main(int argc, char *argv[])
{
int i, rank, commsize, nsteps = 1000000;
double step, local_pi, pi, x, sum = 0.0;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &commsize);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
// Передаем все количество шагов интегрирования
MPI_Bcast(&nsteps, 1, MPI_INT, 0,
MPI_COMM_WORLD);
step = 1.0 / (double)nsteps;
54
Вычисление числа π (parallel code)
// Процесс rank получает точки x:
// rank + 1, rank + 1 + p, rank + 1 + 2p, …
for (i = rank + 1; i <= nsteps; i += commsize) {
x = step * ((double)i - 0.5);
sum += 4.0 / (1.0 + x * x);
}
local_pi = step * sum;
// Формируем результирующую сумму
MPI_Reduce(&local_pi, &pi, 1, MPI_DOUBLE,
MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0)
printf(“PI = %.16fn”, pi);

MPI_Finalize();
return 0;
}

55
Эффективность параллельных программ
 Коэффициент S ускорения параллельной
программы (Speedup)
𝑇1
𝑆= ,
𝑇𝑛
где:
 T1 – время выполнения последовательной программы,

 Tn – время выполнения параллельной программы на n
процессорных ядрах (n процессов)
 Коэффициент E эффективности параллельной
программы
𝑆
𝑇1
𝐸= =
𝑛
𝑛𝑇 𝑛

56
Эффективность программы π
16
14
12
10
8
6
4
2
0

S
Linear

n = 107

n
1

2

4

8

16

Зависимость коэффициента ускорения S
от числа n процессорных ядер
(вычислительные узлы: 2 x Intel Xeon E5420, коммуникационная
сеть Gigabit Ethernet; MPICH2 1.4.1p1, GCC 4.4.6;
на каждом вычислительном узле 1 процесс)
57
Heat 2D
 Уравнение теплопроводности
описывает изменение температуры
в заданной области с течением
времени
 Приближенное решение можно
найти методом конечных разностей
 Область покрывается сеткой

 Производные аппроксимируются
конечными разностями
 Известна температура на границе
области в начальный момент времени

[*] Blaise Barney. Introduction to Parallel Computing (LLNL)

58
Heat 2D (serial code)
 Температура хранится в двумерном массиве – расчетная
область
do iy = 2, ny - 1
do ix = 2, nx - 1
u2(ix, iy) = u1(ix, iy) +
cx * (u1(ix + 1, iy)
u1(ix - 1, iy) – 2.0
cy * (u1(ix, iy + 1)
u1(ix, iy - 1) – 2.0
end do
end do

+
* u1(ix, iy)) +
+
* u1(ix, iy))

[*] Blaise Barney. Introduction to Parallel Computing (LLNL)

59
Heat 2D (parallel version)
 Каждый процесс будет обрабатывать свою часть области
for t = 1 to nsteps
1. Update time
2. Send neighbors my border
3. Receive from neighbors
4. Update my cells
end for

Расчетная область разбита на
вертикальные полосы –
массив распределен

[*] Blaise Barney. Introduction to Parallel Computing (LLNL)

60
Wave Equation

A(i, t + 1) = 2.0 * A(i, t) - A(i, t - 1) +
c * (A(i - 1, t) - (2.0 * A(i, t) +
A(i + 1, t))

[*] Blaise Barney. Introduction to Parallel Computing (LLNL)

61
Wave Equation (data decomposition)

A(i, t + 1) = 2.0 * A(i, t) - A(i, t - 1) +
c * (A(i - 1, t) - (2.0 * A(i, t) +
A(i + 1, t))
Расчетная область разбита на вертикальные полосы –
массив распределен (одномерное разбиение)

[*] Blaise Barney. Introduction to Parallel Computing (LLNL)

62
Intel Trace Analyzer and Collector

 Intel Trace Analyzer & Collector – профилировщик для
MPI-программ, показывает timeline с обменами между процессами,
статистику по переданным сообщениям; позволяет находить
дисбаланс загрузки процессов

63
Профилировщики MPI
 Intel Trace Analyzer & Collector

 mpiP
 VamirTrace + Vampir
 Tau

 Allinea MAP
 MPICH2 MPE (Jumpshot)
 Oracle Solaris Studio (collect)

64
Интерфейс профилирования PMPI
 Каждая функция MPI имеет 2 версии:

 MPI_Function
 PMPI_Function
 Функции с именами MPI_Function можно перехватывать
– подменять функциями врапперами (wrapper) с такими
же именами
 Во врапперах осуществляется регистрация времени
выполнения, собирается статистика по размерам
сообщения и пр.
 Из враппера вызывается оригинальная MPI-функция
PMPI_Function
65
sendprof.c
static int totalBytes = 0;
static double totalTime = 0.0;
// Wrapper for Send
int MPI_Send(const void* buffer, int count,
MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm)
{
double tstart = MPI_Wtime();
int extent;
// Call original (real) send
int rc = PMPI_Send(buffer, count, datatype, dest,
tag, comm);
totalTime += MPI_Wtime() - tstart;
MPI_Type_size(datatype, &extent);
/* Message size */
totalBytes += count * extent;
return rc;
}

66
Использование sendprof.c
$ mpicc –c –o sendprof.o ./sendprof.c
$ ar rcs libsendprof.a sendprof.o
$ mpicc –o mpiprog ./mpiprog.c 
–L. -lsendprof
 Какие функции перехватывать?

 MPI_Initialize(), MPI_Init_threads() –
инициализируем профилировщик
 MPI_Finalize() – вывод отчета
 MPI_Pcontrol() – связь профилировщика
с пользовательской программой
 Если необходимо профилировать Fortran-программы,
то имена врапперов должны соответствовать правилам Fortran
67
(нет указателей, преобразование констант Fortran -> C, …)
MPI Process mapping
Сеть InfiniBand (вычислительная сеть)

Ядро

Процессор

Ядро

Общая память

Ядро

Процессор

Общая память

Frontend (login node)

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 1

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 2

Процессор
Ядро

Ядро

Общая память

Процессор
Ядро

Ядро

Compute node 3

Процесс 2

Процесс 1

Процесс 0

Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …)

Процессор
Ядро

Ядро

Процессор
Ядро

Ядро

Compute node 4

Процесс 3

Ядро

 Время выполнения обменов зависит от того как процессы размещены
по процессорным ядрами (process mapping/pining, task mapping, task
allocation, task assignment)
 Распределение процессов задается при старте программы (mpiexec)
или при формировании виртуальной топологии (MPI Virtual topolgy)

68
Пример вложения программы в ВС
Вложение теста High Performance
Linpack в подсистему:

5

6

7

8

Граф
программы
2

1

3

4

High Performance Linpack (HPL)

Вычислительный кластер
с иерархической структурой
(2 узла по 2 Intel Xeon 5150)

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Кэш L2

Кэш L2

Кэш L2

Кэш L2

Процессор

Процессор

Процессор

Процессор

Общая память

Общая память

Сеть связи Gigabit Ethernet

69
Пример вложения программы в ВС
Вложение теста High Performance
Linpack в подсистему:

5

6

7

8

Граф
программы
2

1

стандартными MPI-утилитами (mpiexec) –
время выполнения 118 сек. (44 GFLOPS)

3

4

High Performance Linpack (HPL)

1

Вычислительный кластер
с иерархической структурой
(2 узла по 2 Intel Xeon 5150)

2

3

4

5

6

7

8

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Кэш L2

Кэш L2

Кэш L2

Кэш L2

Процессор

Процессор

Процессор

Процессор

Общая память

Общая память

Сеть связи Gigabit Ethernet

70
Пример вложения программы в ВС
Вложение теста High Performance
Linpack в подсистему:

5

6

8

Граф
программы
2

1

стандартными MPI-утилитами (mpiexec) –
время выполнения 118 сек. (44 GFLOPS)

с учетом структуры ВС – время
выполнения 100 сек. (53 GFLOPS)

7

3

4

High Performance Linpack (HPL)

2

3

4

5

6

7

8

1

Вычислительный кластер
с иерархической структурой
(2 узла по 2 Intel Xeon 5150)

1

3

5

7

2

4

6

8

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Ядро

Кэш L2

Кэш L2

Кэш L2

Кэш L2

Процессор

Процессор

Процессор

Процессор

Общая память

Общая память

Сеть связи Gigabit Ethernet

71
Иерархический метод вложения программ
Метод вложения на основе многоуровневых (multilevel) алгоритмов разбиения
графов G = (V, E) параллельных программ
1. Граф G разбивается на k подмножеств; k – количество узлов, составляющих ВС. В каждое из
подмножеств включаются ветви, интенсивно обменивающиеся данными.
2. Параллельные ветви из i-го подмножества распределяются по ядрам i-го вычислительного
узла, 𝑖 ∈ {1, 2, . . , 𝑘}.
1

5

9

13

2

6

10

14

3

7

11

15

4

Ядро

Ядро

Процессор

8

V1
Ядро

Ядро

Процессор

Разделяемая память

Ядро

Ядро

Процессор

12

V2
Ядро

Ядро

Процессор

Разделяемая память

16

V3
Ядро

Ядро

Процессор

Ядро

Ядро

Процессор

Разделяемая память

V4
Ядро

Ядро

Процессор

Ядро

Ядро

Процессор

Разделяемая память

Сеть связи Gigabit Ethernet

Вложение MPI-программы Conjugate Gradient (CG) из пакета NAS Parallel Benchmarks,
реализующей решение системы линейных алгебраических уравнений методом
72
сопряженных градиентов в вычислительный кластер
Иерархический метод вложения программ

The Parallel Ocean Program, NP = 128
Моделирование процессов в мировом океане

73
Иерархический метод вложения программ
Подсистем 1 (8 ядер)

Подсистема 2 (16 ядер)

Подсистема 3 (16 ядер)

The Parallel Ocean Program (POP), NP = 40

74
Иерархический метод вложения программ
Подсистем 1 (8 ядер)

Подсистема 2 (16 ядер)

Подсистема 3 (16 ядер)

The Parallel Ocean Program (POP), NP = 40

75
Иерархический метод вложения программ
Подсистем 1 (8 ядер)

Подсистема 2 (16 ядер)

Подсистема 3 (16 ядер)

The Parallel Ocean Program (POP), NP = 40

76

Стандарт MPI (Message Passing Interface)

  • 1.
    Лекция 10: Стандарт MPI (MPI– Message Passing Interface) Курносов Михаил Георгиевич к.т.н. доцент Кафедры вычислительных систем Сибирский государственный университет телекоммуникаций и информатики http://www.mkurnosov.net
  • 2.
    Стандарт MPI  MessagePassing Interface (MPI) – это стандарт на программный интерфейс коммуникационных библиотек для создания параллельных программ в модели передачи сообщений (message passing)  Стандарт определяет интерфейс для языков программирования C и Fortran MPI 1.0 MPI 1.1 MPI 1.2, 2.0 MPI 1.3, 2.1 MPI 2.2 MPI 3.0 1994 1995 1997 2008 2009 2012 http://www.mpi-forum.org http://www.mpi-forum.org/docs/mpi-3.0/mpi30-report.pdf 2
  • 3.
    Реализации стандарт MPI(библиотеки)  MPICH2 (Open source, Argone NL) http://www.mcs.anl.gov/research/projects/mpich2  MVAPICH2  IBM MPI  Cray MPI  Intel MPI  HP MPI  SiCortex MPI  Open MPI (Open source, BSD License) http://www.open-mpi.org  Oracle MPI 3
  • 4.
    Различия в реализацияхстандарта MPI  Спектр поддерживаемых архитектур процессоров: Intel, IBM, ARM, Fujitsu, NVIDIA, AMD  Типы поддерживаемых коммуникационных технологий/сетей: InfiniBand, 10 Gigabit Ethernet, Cray Gemeni, IBM PERCS/5D torus, Fujitsu Tofu, Myrinet, SCI  Реализованные протоколы дифференцированных обменов (Point-to-point): хранение списка процессов, подтверждение передачи (ACK), буферизация сообщений, …  Коллективные операции обменов информацией: коммуникационная сложность алгоритмов, учет структуры вычислительной системы (torus, fat tree, …), неблокирующие коллективные обмены (MPI 3.0, методы хранение collective schedule)  Алгоритмы вложения графов программ в структуры вычислительных систем (MPI topology mapping)  Возможность выполнения MPI-функций в многопоточной среде и поддержка ускорителей (GPU NVIDIA/AMD, Intel Xeon Phi) 4
  • 5.
    Стандарт MPI Сеть InfiniBand(вычислительная сеть) Общая память Ядро Ядро Процессор Ядро Процессор Ядро Ядро Compute node 1 Процесс 0 Frontend (login node) Процессор Ядро Общая память Процессор Ядро Ядро Compute node 2 Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 3 Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …) Процессор Ядро Ядро Процессор Ядро Ядро Compute node 4 Процесс 2 Процессор Общая память Процесс 3 Общая память Ядро Процесс 5 Процессор Ядро Процесс 4 Ядро Процесс 1 Ядро MPI_COMM_WORLD  Коммуникатор (communicator) – множество процессов, образующих логическую область для выполнения коллективных операций (обменов информацией и др.)  В рамках коммуникатора ветви имеют номера: 0, 1, …, n – 1 5
  • 6.
    MPI: Hello World #include<mpi.h> int main(int argc, char *argv[]) { int rank, commsize; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &commsize); MPI_Comm_rank(MPI_COMM_WORLD, &rank); printf(“Hello, World: process %d of %dn”, rank, commsize); MPI_Finalize(); return 0; } 6
  • 7.
    Компиляция MPI-программ Компиляция C-программы: $mpicc –o prog ./prog.c Компиляция C++-программы: $ mpicxx –o prog ./prog.cpp Компиляция Fortran-программы: $ mpif90 –o prog ./prog.f Компиляция C-программы компилятором Intel C/C++: $ mpicc –cc=icc –o prog ./prog.c 7
  • 8.
    Запуск MPI-программ накластере Jet 1. Формируем job-файл с ресурсным запросом (task.job): #PBS –N MyTask #PBS -l nodes=4:ppn=1 # 4 узла, 1 процесс на узел #PBS -j oe cd $PBS_O_WORKDIR mpiexec ./prog 2. Ставим задачу в очередь системы пакетной обработки заданий TORQUE $ qsub task.job 7719.jet Job ID 8
  • 9.
    Выполнение MPI-программ Сеть InfiniBand(вычислительная сеть) Ядро Процессор Ядро Общая память Ядро Процессор Общая память Frontend (login node) Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 1 Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 2 Процессор Ядро Общая память Процессор Ядро Ядро Ядро Compute node 3 Процессор Ядро Ядро Процесс 2 Процесс 1 Ядро Ядро Compute node 4 Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …) Процесс 0 Процессор Процесс 3 Ядро MPI_COMM_WORLD  Система TORQUE запустит задачу, когда для нее будут доступны ресурсы  В данном примере на каждом узле могут также присутствовать 3 процесса других задач (пользователей) 9
  • 10.
    Состояние заданий вочереди TORQUE $ qstat Job id Name User Time Use S Queue ------------- ---------------- --------------- -------- - ----- 7732.jet simmc mkurnosov 00:00:00 C debug 7733.jet Heat2D mkurnosov 0 R debug 7734.jet simmc mkurnosov 00:00:00 C debug 7735.jet mpitask mkurnosov 00:00:00 C debug 7736.jet simmc mkurnosov 00:00:00 C debug 7737.jet mpitask mkurnosov 00:00:00 C debug 7738.jet Heat2D mkurnosov 0 R debug Job state  C – completed  R – running  Q – waiting in queue 10
  • 11.
    Формирование ресурсных запросов(Job)  8 ветвей – все ветви на одном узле #PBS -l nodes=1:ppn=8  6 ветвей – две ветви на каждом узле #PBS -l nodes=3:ppn=2  Команды управления задачами в очереди o qdel – удаление задачи из очереди o qstat – состояние очереди o pbsnodes – состояние вычислительных узлов 11
  • 12.
    Стандарт MPI  Двусторонниеобмены (Point-to-point communication)  Составные типы данных (Derived datatypes)  Коллективные обмены (Collective communication)  Группы процессов, коммуникаторы (Groups, communicators)  Виртуальные топологии процессов  Управление средой выполнения (Environmental management)  Управление процессами (Process management)  Односторонние обмены (One-sided communication)  Файловый ввод-вывод (I/O) 12
  • 13.
    Двусторонние обмены (Point-to-point) Двусторонний обмен (Point-to-point communication) – это обмен сообщением между двумя процессами: один инициирует передачу (send), а другой – получение сообщения (receive)  Каждое сообщение снабжается конвертом (envelope): − номер процесса отправителя (source) − номер процесса получателя (destination) − тег сообщения (tag) − коммуникатор (communicator) 13
  • 14.
    Двусторонние обмены (Point-to-point) Блокирующие(Blocking)         MPI_Bsend MPI_Recv MPI_Rsend MPI_Send MPI_Sendrecv MPI_Sendrecv_replace MPI_Ssend ... Проверки состояния запросов (Completion/Testing)      MPI_Iprobe MPI_Probe MPI_Test{, all, any, some} MPI_Wait{, all, any, some} ... Неблокирующие (Non-blocking)       MPI_Ibsend MPI_Irecv MPI_Irsend MPI_Isend MPI_Issend ... Постоянные (Persistent)       MPI_Bsend_init MPI_Recv_init MPI_Send_init ... MPI_Start MPI_Startall 14
  • 15.
    MPI_Send/MPI_Recv int MPI_Send(void *buf, intcount, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)     buf – указатель на буфер с информацией count – количество передаваемых элементов типа datatype dest – номер процесса получателя сообщения tag – тег сообщения int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status) 15
  • 16.
    Пример MPI_Send/MPI_Recv int main(intargc, char **argv) { int rank; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) MPI_Send(buf, BUFSIZE, MPI_DOUBLE, 1, 1, MPI_COMM_WORLD); else MPI_Recv(buf, BUFSIZE, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD, &status); MPI_Finalize(); return 0; } 16
  • 17.
    Пример MPI_Send/MPI_Recv int main(intargc, char **argv) { int rank; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); Infinite waiting if (rank == 0) MPI_Send(buf, BUFSIZE, MPI_DOUBLE, 1, 1, (if commsize > 2) MPI_COMM_WORLD); else MPI_Recv(buf, BUFSIZE, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD, &status); MPI_Finalize(); return 0; } 17
  • 18.
    Бесконечное ожидание (commsize= 3) Сеть InfiniBand (вычислительная сеть) Общая память Ядро Процессор Общая память Frontend (login node) Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 1 Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 2 MPI_Send MPI_Recv Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 3 Процессор Ядро Ядро Процессор Ядро Ядро Compute node 4 Процесс 2 Процессор Ядро Процесс 1 Ядро Процесс 0 Ядро Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …) MPI_Recv ∞ waiting for a message Нет парного MPI_Send 18
  • 19.
    Пример MPI_Send/MPI_Recv int main(intargc, char **argv) { int rank; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) MPI_Send(buf, BUFSIZE, MPI_DOUBLE, 1, 1, MPI_COMM_WORLD); else if (rank == 1) MPI_Recv(buf, BUFSIZE, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD, &status); MPI_Finalize(); return 0; } 19
  • 20.
    Режимы передачи сообщений Standard (MPI_Send) – библиотека сама принимает решение буферизовать сообщение и сразу выходить из функции или дожидаться окончания передачи данных получателю (например, если нет свободного буфера)  Buffered (MPI_Bsend) – отправляемое сообщение помещается в буфер и функция завершает свою работу. Сообщение будет отправлено библиотекой.  Synchronous (MPI_Ssend) – функция завершает своё выполнение когда процесс-получатель вызвал MPI_Recv и начал копировать сообщение. После вызова функции можно безопасно использовать буфер.  Ready (MPI_Rsend) – функция успешно выполняется только если процесс-получатель уже вызвал функцию MPI_Recv 20
  • 21.
    Неблокирующие двусторонние обмены Неблокирующие передача/прием сообщений (Nonblocking point-to-point communication) – операция, выход из которой осуществляется не дожидаясь завершения передачи информации  Пользователю возвращается дескриптор запроса (request), который он может использовать для проверки состояния операции  Цель – обеспечить возможность совмещения вычислений и обменов информацией (Overlap computations & communications) int MPI_Isend(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) 21
  • 22.
    Network offload Высокопроизводительные сетевыеконтроллеры аппаратурно реализуют часть функций сетевого стека => низкая латентность передачи сообщений, совмещение обменов и вычислений MPI TCP/UDP IP MAC PHY MPI Операционная система Сетевой интерфейс Операционная система TCP/UDP IP MAC TOE сетевой интерфейс PHY 22
  • 23.
  • 24.
    Неблокирующие двусторонние обмены int MPI_Wait(MPI_Request *request, MPI_Status *status) Блокирует выполнение процесса пока не закончится операция, ассоциированная с запросом request  int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status) Возвращает flag = 1 если операция, ассоциированная с запросом request, завершена 24
  • 25.
    Совмещение обменов ивычислений MPI_Isend(buf, count, MPI_INT, 1, 0, MPI_COMM_WORLD, &req); do { // // Вычисления ... (не использовать buf) // MPI_Test(&req, &flag, &status); } while (!flag) 25
  • 26.
    Неблокирующие двусторонние обмены int MPI_Waitany(int count, MPI_Request array_of_requests[], int *index, MPI_Status *status)  int MPI_Waitall(int count, MPI_Request array_of_requests[], MPI_Status array_of_statuses[])  int MPI_Testany(int count, MPI_Request array_of_requests[], int *index, int *flag, MPI_Status *status)  int MPI_Testall(int count, MPI_Request array_of_requests[], int *flag, MPI_Status array_of_statuses[]) 26
  • 27.
    Типы данных MPI(Datatypes)             MPI_PACKED 1 MPI_BYTE 1 MPI_CHAR 1 MPI_UNSIGNED_CHAR 1 MPI_SIGNED_CHAR 1 MPI_WCHAR 2 MPI_SHORT 2 MPI_UNSIGNED_SHORT 2 MPI_INT 4 MPI_UNSIGNED 4 MPI_LONG 4 MPI_UNSIGNED_LONG 4           MPI_FLOAT 4 MPI_DOUBLE 8 MPI_LONG_DOUBLE 16 MPI_CHARACTER 1 MPI_LOGICAL 4 MPI_INTEGER 4 MPI_REAL 4 MPI_DOUBLE_PRECISION 8 MPI_COMPLEX 2*4 MPI_DOUBLE_COMPLEX 2*8 В MPI имеется возможность создавать пользовательские типы данных (Derived Datatypes, структуры, части массивов) 27
  • 28.
    Коллективные обмены (блокирующие) Трансляционныйобмен (One-to-all) o MPI_Bcast o MPI_Scatter o MPI_Scatterv Коллекторный обмен (All-to-one) o MPI_Gather o MPI_Gatherv o MPI_Reduce Трансляционно-циклический обмен (All-to-all) o MPI_Allgather o MPI_Allgatherv o MPI_Alltoall o MPI_Alltoallv o MPI_Allreduce o MPI_Reduce_scatter 28
  • 29.
    MPI_Bcast (One-to-all broadcast) intMPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm) Элементы A A MPI_Bcast Рассылка всем одинакового сообщения Процессы Процессы Элементы A A A  MPI_Bcast – рассылка всем процессам сообщения buf  Если номер процесса совпадает с root, то он отправитель, иначе – приемник 29
  • 30.
    MPI_Bcast (One-to-all broadcast) intMPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm) int main(int argc, char *argv[]) { double buf[n]; MPI_Init(&argc,&argv); /* Code */ // Передать массив buf всем процессам (и себе) MPI_Bcast(&buf, n, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Finalize(); return 0; } 30
  • 31.
    MPI_Scatter (One-to-all broadcast) intMPI_Scatter(void *sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcnt, MPI_Datatype recvtype, int root, MPI_Comm comm) A0 A1 A2 Элементы A3 MPI_Scatter Рассылка всем разных сообщений Процессы Процессы Элементы A0 A1 A2 A3  Размер sendbuf = sizeof(sendtype) * sendcnt * commsize  Размер recvbuf = sizeof(sendtype) * recvcnt 31
  • 32.
    MPI_Gather (All-to-one) int MPI_Gather(void*sendbuf, int sendcnt, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm) A0 A1 A2 Элементы A3 MPI_Gather Прием от всех разных сообщений Процессы Процессы Элементы A0 A1 A2 A3  Размер sendbuf: sizeof(sendtype) * sendcnt  Размер recvbuf: sizeof(sendtype) * sendcnt * commsize 32
  • 33.
    MPI_Reduce (All-to-one) int MPI_Reduce(void*sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) Rank 0 (root) Rank 1 Rank 2 1 2 3 1 7 4 5 6 1 3 7 8 9 9 3 + + + 12 15 18 11 13 recvbuf Формирование в процессе root массива значений, полученных применением операции op  Размер sendbuf: sizeof(datatype) * count  Размер recvbuf: sizeof(datatype) * count 33
  • 34.
    Операции MPI_Reduce       MPI_MAX MPI_MIN MPI_MAXLOC MPI_MINLOC MPI_SUM MPI_PROD       MPI_LAND MPI_LOR MPI_LXOR MPI_BAND MPI_BOR MPI_BXOR int MPI_Op_create(MPI_User_function*function, int commute, MPI_Op *op)  Операция пользователя должна быть ассоциативной A * (B * C) = (A * B) * C  Если commute = 1, то операция коммутативная A*B=B*A 34
  • 35.
    Пользовательская операция MPI_Reduce typedefstruct { double real, imag; } Complex; Complex sbuf[100], rbuf[100]; MPI_Op complexmulop; MPI_Datatype ctype; MPI_Type_contiguous(2, MPI_DOUBLE, &ctype); MPI_Type_commit(&ctype); // Умножение комплексных чисел MPI_Op_create(complex_mul, 1, &complexmulop); MPI_Reduce(sbuf, rbuf, 100, ctype, complexmulop, root, comm); MPI_Op_free(&complexmulop); MPI_Type_free(&ctype); 35
  • 36.
    Пользовательская операция MPI_Reduce //Умножение массивов комплексных чисел void complex_mul(void *inv, void *inoutv, int *len, MPI_Datatype *datatype) { int i; Complex c; Complex *in = (Complex *)inv; *inout = (Complex *)inoutv; for (i = 0; i < *len; i++) c.real = inout->real * inout->imag * c.imag = inout->real * inout->imag * *inout = c; in++; inout++; } { in->real in->imag; in->imag + in->real; } 36
  • 37.
    MPI_Alltoall int MPI_Alltoall(void *sendbuf,int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcnt, MPI_Datatype recvtype, MPI_Comm comm) Элементы A0 A1 A2 A3 B0 B1 B2 B3 C0 C1 C2 C3 D0 D1 D2 D3 MPI_Alltoall В каждом процессе собираются сообщения всех процессов Процессы Процессы Элементы A0 B0 C0 D0 A1 B1 C1 D1 A2 B2 C2 D2 A3 B3 C3 D3  Размер sendbuf: sizeof(sendtype) * sendcount * commsize  Размер recvbuf: sizeof(recvtype) * recvcount * commsize 37
  • 38.
    MPI_Barrier int MPI_Barrier(MPI_Comm comm) Барьерная синхронизация процессов – ни один процесс не завершит выполнение этой функции пока все не войдут в неё int main(int argc, char *argv[]) { /* Code */ MPI_Barrier(MPI_COMM_WORLD); /* Code */ MPI_Finalize(); return 0; } 38
  • 39.
    All-to-all int MPI_Allgather(void *sendbuf,int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm) int MPI_Allgatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int *recvcounts, int *displs, MPI_Datatype recvtype, MPI_Comm comm) int MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) 39
  • 40.
    Реализация коллективных операций Аппаратная реализация коллективных операций – специализированные коммуникационные сети: IBM BlueGene/P Tree network, barrier network  Программная реализация – коллективная операция реализуется на основе операций Send/Recv  Программная реализация коллективных операций имеет наибольшее распространение (MPICH2, Open MPI) 40
  • 41.
    Алгоритмы коллективных операций One-to-allBroadcast (MPI_Bcast, MPI_Scatter)  Алгоритм биномиального дерева (Binomial tree)  Scatter + Allgather All-to-one Broadcast (MPI_Reduce, MPI_Gather):  Алгоритм биномиального дерева  Reduce_Scatter + Gather а0 + a1 0 MPI_Recv а0 + a1 + а2 + a3 MPI_Recv а0 + … + a5 MPI_Recv a1 All-to-all Broadcast (MPI_Allgather, MPI_Allreduce) 1 2 MPI_Send MPI_Recv а2 + a3 MPI_Send a4 + а5 a3   Алгоритм рекурсивного сдваивания (Recursive doubling) Алгоритм Дж. Брука (J. Bruck, 1997) 3 4 MPI_Send MPI_Recv MPI_Send a5 5 MPI_Send t Диаграмма выполнения операции MPI_Reduce алгоритмом биномиального дерева (суммирование элементов 6 ветвей) 41
  • 42.
    Реализация коллективных операций Как правило, для каждой коллективной операции имеется несколько алгоритмов ее реализации – выбор алгоритма основан на эвристике int MPIR_Allgather_intra(...) mpich2-3.0.4 { // ... tot_bytes = (MPI_Aint)recvcount * comm_size * type_size; if ((tot_bytes < MPIR_PARAM_ALLGATHER_LONG_MSG_SIZE) && !(comm_size & (comm_size - 1))) { /* Short or medium size message and power-of-two no. of processes. Use recursive doubling algorithm */ // ... else if (tot_bytes < MPIR_PARAM_ALLGATHER_SHORT_MSG_SIZE) { /* Short message and non-power-of-two no. of processes. Use Bruck algorithm (see description above). */ // ... else { /* Long message or medium-size message and non-power-of-two no. of processes. Use ring algorithm. */ // ... } } 42
  • 43.
    Выбор алгоритма коллективнойоперации  Как динамически (во время вызова функции) выбирать алгоритм реализации коллективной операции?  Учитывать только размер сообщения? Такой выбор будет оптимальным только для той системы, на которой провели предварительные замеры времени выполнения операций (MPICH2, Open MPI)  Использовать данные предварительных замеров времени выполнения алгоритмов (при установке библиотеки на вычислительную систему)?  Оценивать время выполнения алгоритма на модели (LogP, LogGP, PLogP, …)? 43
  • 44.
    Алгоритмы реализации MPI_Allgather Кольцевойалгоритм (ring) a0 a1 a2 a3 0 a0 a1 a2 a3 3 1 a0 a1 a2 a3 2 a0 a1 a2 a3 Каждая ветвь выполняет 2(n – 1) обменов (Send/Recv) 44
  • 45.
    Алгоритмы реализации MPI_Allgather Алгоритмрекурсивного сдваивания (recursive doubling) Алгоритм Дж. Брука (J. Bruck et al., 1997) 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a0 a1 a2 a3 a4 a5 a6 a7 a1 0 a2 1 a3 2 a4 3 a5 4 a6 5 a7 6 a0 7 a2 0 a3 1 a4 2 a5 3 a6 4 a7 5 a0 6 a1 7 a3 0 a4 1 a5 2 a6 3 a7 4 a0 5 a1 6 a2 7 a4 0 a5 1 a6 2 a7 3 a0 4 a1 5 a2 6 a3 7 a5 0 a6 1 a7 2 a0 3 a1 4 a2 5 a3 6 a4 7 a6 0 a7 1 a0 2 a1 3 a2 4 a3 5 a4 6 a5 7 a7 0 a0 1 a1 2 a2 3 a3 4 a4 5 a5 6 a6 7 Количество обменов: 2log2n Только для n равного степени двойки Количество обменов: 2log2n На шаге k ветвь i взаимодействует с ветвями (i – 2k + n) mod n и (i + 2k) mod n На каждом шаге размер передаваемого блока удваивается: m, 2m, 4m 45
  • 46.
    Таймер double MPI_Wtime() int main(intargc, char **argv) { double t = 0.0 t -= MPI_Wtime(); /* Code */ t += MPI_Wtime(); printf(“Elapsed time: %.6f secondsn", t); } 46
  • 47.
    Неблокирующие коллективные операции MPI3.0  Неблокирующий коллективный обмен (Non-blocking collective communication) – коллективная операция, выход из которой осуществляется не дожидаясь завершения операций обменов  Пользователю возвращается дескриптор запроса (request), который он может использовать для проверки состояния операции  Цель – обеспечить возможность совмещения вычислений и обменов информацией int MPI_Ibcast(void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm, MPI_Request *request) 47
  • 48.
    MPI_Ibcast MPI 3.0 MPI_Request req; MPI_Ibcast(buf,count, MPI_INT, 0, MPI_COMM_WORLD, &req); while (!flag) { // Вычисления... // Проверяем состояние операции MPI_Test(&req, &flag, MPI_STATUS_IGNORE); } MPI_Wait(&req, MPI_STATUS_IGNORE); 48
  • 49.
    Non-blocking MPI Collectives MPI3.0 Блокирующие коллективные операции (Blocking MPI Collectives) MPI_Bcast MPI lib. Waiting User code Computations t tblocking Неблокирующие коллективные операции (Non-blocking collectives) MPI_Wait MPI_Ibcast MPI lib. Computations User code tinit tcomp toverlap t twait 49
  • 50.
    Non-blocking MPI Collectives MPI3.0 Блокирующие коллективные операции Коэффициент 𝜶 совмещения вычисления (Blocking MPI Collectives) MPI lib. и обменов информацией MPI_Bcast 𝒕 𝒐𝒗𝒆𝒓𝒍𝒂𝒑 Computations − 𝒕 𝒄𝒐𝒎𝒑 𝜶= 𝟏− tblocking 𝒕 𝒃𝒍𝒐𝒄𝒌𝒊𝒏𝒈 Waiting User code t Неблокирующие коллективные операции (Non-blocking collectives) MPI_Wait MPI_Ibcast MPI lib. Computations User code tinit tcomp toverlap t twait 50
  • 51.
    Неблокирующие коллективные операции При вызове неблокирующией коллективной операции создается расписание выполнения обменов (collective schedule)  Progress engine – механизм, который в фоновом режиме реализует обмены по созданному расписанию – как правило обмены выполняются при вызове MPI_Test (в противном случае необходим дополнительный поток) MPI_Request req; MPI_Ibcast(buf, count, MPI_INT, 0, MPI_COMM_WORLD, &req); while (!flag) { // Вычисления... // Проверяем состояние и продвигаем обмены по расписанию MPI_Test(&req, &flag, MPI_STATUS_IGNORE); } MPI_Wait(&req, MPI_STATUS_IGNORE); 51
  • 52.
    Вычисление числа π 1 𝜋= 0 4 𝑑𝑥 2 1+𝑥 𝑛 𝜋≈ℎ 𝑖=1 4 1 + (ℎ(𝑖 − 0.5))2 1 ℎ= 𝑛 4,50 4,00 3,50 3,00 2,50 2,00 1,50 1,00 0,50 0,00 0,00 0,10 0,20 0,30 0,40 0,50 0,60 0,70 0,80 0,90 52
  • 53.
    Вычисление числа π(sequential version) int main() { int i, nsteps = 1000000; double step = 1.0 / (double)nsteps; double pi, x, sum = 0.0; for (i = 1; i <= nsteps; i++) { x = step * ((double)i - 0.5); sum += 4.0 / (1.0 + x * x); } pi = step * sum; printf(“PI = %.16fn”, pi); return 0; } 53
  • 54.
    Вычисление числа π(parallel version) int main(int argc, char *argv[]) { int i, rank, commsize, nsteps = 1000000; double step, local_pi, pi, x, sum = 0.0; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &commsize); MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Передаем все количество шагов интегрирования MPI_Bcast(&nsteps, 1, MPI_INT, 0, MPI_COMM_WORLD); step = 1.0 / (double)nsteps; 54
  • 55.
    Вычисление числа π(parallel code) // Процесс rank получает точки x: // rank + 1, rank + 1 + p, rank + 1 + 2p, … for (i = rank + 1; i <= nsteps; i += commsize) { x = step * ((double)i - 0.5); sum += 4.0 / (1.0 + x * x); } local_pi = step * sum; // Формируем результирующую сумму MPI_Reduce(&local_pi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (rank == 0) printf(“PI = %.16fn”, pi); MPI_Finalize(); return 0; } 55
  • 56.
    Эффективность параллельных программ Коэффициент S ускорения параллельной программы (Speedup) 𝑇1 𝑆= , 𝑇𝑛 где:  T1 – время выполнения последовательной программы,  Tn – время выполнения параллельной программы на n процессорных ядрах (n процессов)  Коэффициент E эффективности параллельной программы 𝑆 𝑇1 𝐸= = 𝑛 𝑛𝑇 𝑛 56
  • 57.
    Эффективность программы π 16 14 12 10 8 6 4 2 0 S Linear n= 107 n 1 2 4 8 16 Зависимость коэффициента ускорения S от числа n процессорных ядер (вычислительные узлы: 2 x Intel Xeon E5420, коммуникационная сеть Gigabit Ethernet; MPICH2 1.4.1p1, GCC 4.4.6; на каждом вычислительном узле 1 процесс) 57
  • 58.
    Heat 2D  Уравнениетеплопроводности описывает изменение температуры в заданной области с течением времени  Приближенное решение можно найти методом конечных разностей  Область покрывается сеткой  Производные аппроксимируются конечными разностями  Известна температура на границе области в начальный момент времени [*] Blaise Barney. Introduction to Parallel Computing (LLNL) 58
  • 59.
    Heat 2D (serialcode)  Температура хранится в двумерном массиве – расчетная область do iy = 2, ny - 1 do ix = 2, nx - 1 u2(ix, iy) = u1(ix, iy) + cx * (u1(ix + 1, iy) u1(ix - 1, iy) – 2.0 cy * (u1(ix, iy + 1) u1(ix, iy - 1) – 2.0 end do end do + * u1(ix, iy)) + + * u1(ix, iy)) [*] Blaise Barney. Introduction to Parallel Computing (LLNL) 59
  • 60.
    Heat 2D (parallelversion)  Каждый процесс будет обрабатывать свою часть области for t = 1 to nsteps 1. Update time 2. Send neighbors my border 3. Receive from neighbors 4. Update my cells end for Расчетная область разбита на вертикальные полосы – массив распределен [*] Blaise Barney. Introduction to Parallel Computing (LLNL) 60
  • 61.
    Wave Equation A(i, t+ 1) = 2.0 * A(i, t) - A(i, t - 1) + c * (A(i - 1, t) - (2.0 * A(i, t) + A(i + 1, t)) [*] Blaise Barney. Introduction to Parallel Computing (LLNL) 61
  • 62.
    Wave Equation (datadecomposition) A(i, t + 1) = 2.0 * A(i, t) - A(i, t - 1) + c * (A(i - 1, t) - (2.0 * A(i, t) + A(i + 1, t)) Расчетная область разбита на вертикальные полосы – массив распределен (одномерное разбиение) [*] Blaise Barney. Introduction to Parallel Computing (LLNL) 62
  • 63.
    Intel Trace Analyzerand Collector  Intel Trace Analyzer & Collector – профилировщик для MPI-программ, показывает timeline с обменами между процессами, статистику по переданным сообщениям; позволяет находить дисбаланс загрузки процессов 63
  • 64.
    Профилировщики MPI  IntelTrace Analyzer & Collector  mpiP  VamirTrace + Vampir  Tau  Allinea MAP  MPICH2 MPE (Jumpshot)  Oracle Solaris Studio (collect) 64
  • 65.
    Интерфейс профилирования PMPI Каждая функция MPI имеет 2 версии:  MPI_Function  PMPI_Function  Функции с именами MPI_Function можно перехватывать – подменять функциями врапперами (wrapper) с такими же именами  Во врапперах осуществляется регистрация времени выполнения, собирается статистика по размерам сообщения и пр.  Из враппера вызывается оригинальная MPI-функция PMPI_Function 65
  • 66.
    sendprof.c static int totalBytes= 0; static double totalTime = 0.0; // Wrapper for Send int MPI_Send(const void* buffer, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) { double tstart = MPI_Wtime(); int extent; // Call original (real) send int rc = PMPI_Send(buffer, count, datatype, dest, tag, comm); totalTime += MPI_Wtime() - tstart; MPI_Type_size(datatype, &extent); /* Message size */ totalBytes += count * extent; return rc; } 66
  • 67.
    Использование sendprof.c $ mpicc–c –o sendprof.o ./sendprof.c $ ar rcs libsendprof.a sendprof.o $ mpicc –o mpiprog ./mpiprog.c –L. -lsendprof  Какие функции перехватывать?  MPI_Initialize(), MPI_Init_threads() – инициализируем профилировщик  MPI_Finalize() – вывод отчета  MPI_Pcontrol() – связь профилировщика с пользовательской программой  Если необходимо профилировать Fortran-программы, то имена врапперов должны соответствовать правилам Fortran 67 (нет указателей, преобразование констант Fortran -> C, …)
  • 68.
    MPI Process mapping СетьInfiniBand (вычислительная сеть) Ядро Процессор Ядро Общая память Ядро Процессор Общая память Frontend (login node) Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 1 Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 2 Процессор Ядро Ядро Общая память Процессор Ядро Ядро Compute node 3 Процесс 2 Процесс 1 Процесс 0 Сеть Gigabit Ethernet (сервисная сеть: NFS, DNS, DHCP, ssh, …) Процессор Ядро Ядро Процессор Ядро Ядро Compute node 4 Процесс 3 Ядро  Время выполнения обменов зависит от того как процессы размещены по процессорным ядрами (process mapping/pining, task mapping, task allocation, task assignment)  Распределение процессов задается при старте программы (mpiexec) или при формировании виртуальной топологии (MPI Virtual topolgy) 68
  • 69.
    Пример вложения программыв ВС Вложение теста High Performance Linpack в подсистему: 5 6 7 8 Граф программы 2 1 3 4 High Performance Linpack (HPL) Вычислительный кластер с иерархической структурой (2 узла по 2 Intel Xeon 5150) Ядро Ядро Ядро Ядро Ядро Ядро Ядро Ядро Кэш L2 Кэш L2 Кэш L2 Кэш L2 Процессор Процессор Процессор Процессор Общая память Общая память Сеть связи Gigabit Ethernet 69
  • 70.
    Пример вложения программыв ВС Вложение теста High Performance Linpack в подсистему: 5 6 7 8 Граф программы 2 1 стандартными MPI-утилитами (mpiexec) – время выполнения 118 сек. (44 GFLOPS) 3 4 High Performance Linpack (HPL) 1 Вычислительный кластер с иерархической структурой (2 узла по 2 Intel Xeon 5150) 2 3 4 5 6 7 8 Ядро Ядро Ядро Ядро Ядро Ядро Ядро Ядро Кэш L2 Кэш L2 Кэш L2 Кэш L2 Процессор Процессор Процессор Процессор Общая память Общая память Сеть связи Gigabit Ethernet 70
  • 71.
    Пример вложения программыв ВС Вложение теста High Performance Linpack в подсистему: 5 6 8 Граф программы 2 1 стандартными MPI-утилитами (mpiexec) – время выполнения 118 сек. (44 GFLOPS) с учетом структуры ВС – время выполнения 100 сек. (53 GFLOPS) 7 3 4 High Performance Linpack (HPL) 2 3 4 5 6 7 8 1 Вычислительный кластер с иерархической структурой (2 узла по 2 Intel Xeon 5150) 1 3 5 7 2 4 6 8 Ядро Ядро Ядро Ядро Ядро Ядро Ядро Ядро Кэш L2 Кэш L2 Кэш L2 Кэш L2 Процессор Процессор Процессор Процессор Общая память Общая память Сеть связи Gigabit Ethernet 71
  • 72.
    Иерархический метод вложенияпрограмм Метод вложения на основе многоуровневых (multilevel) алгоритмов разбиения графов G = (V, E) параллельных программ 1. Граф G разбивается на k подмножеств; k – количество узлов, составляющих ВС. В каждое из подмножеств включаются ветви, интенсивно обменивающиеся данными. 2. Параллельные ветви из i-го подмножества распределяются по ядрам i-го вычислительного узла, 𝑖 ∈ {1, 2, . . , 𝑘}. 1 5 9 13 2 6 10 14 3 7 11 15 4 Ядро Ядро Процессор 8 V1 Ядро Ядро Процессор Разделяемая память Ядро Ядро Процессор 12 V2 Ядро Ядро Процессор Разделяемая память 16 V3 Ядро Ядро Процессор Ядро Ядро Процессор Разделяемая память V4 Ядро Ядро Процессор Ядро Ядро Процессор Разделяемая память Сеть связи Gigabit Ethernet Вложение MPI-программы Conjugate Gradient (CG) из пакета NAS Parallel Benchmarks, реализующей решение системы линейных алгебраических уравнений методом 72 сопряженных градиентов в вычислительный кластер
  • 73.
    Иерархический метод вложенияпрограмм The Parallel Ocean Program, NP = 128 Моделирование процессов в мировом океане 73
  • 74.
    Иерархический метод вложенияпрограмм Подсистем 1 (8 ядер) Подсистема 2 (16 ядер) Подсистема 3 (16 ядер) The Parallel Ocean Program (POP), NP = 40 74
  • 75.
    Иерархический метод вложенияпрограмм Подсистем 1 (8 ядер) Подсистема 2 (16 ядер) Подсистема 3 (16 ядер) The Parallel Ocean Program (POP), NP = 40 75
  • 76.
    Иерархический метод вложенияпрограмм Подсистем 1 (8 ядер) Подсистема 2 (16 ядер) Подсистема 3 (16 ядер) The Parallel Ocean Program (POP), NP = 40 76