- •Модели программирования систем класса mimd. Стандарт mpi. Операции поддерживаемые и неподдерживаемые в mpi. Содержимое реализации стандарта mpi. Структура mpi-программы.
- •Стандарт mpi. Идентификация в mpi. Цели разработчиков mpi. Классификация функций mpi. Структура mpi-программы. Пример программы.
- •Двухточечные передачи в mpi. Организация отложенных посылок/приемов сообщений. Функции (основные и вспомогательные), используемые при отложенных посылках и приемах сообщений. Примеры.
- •Типы данных в mpi-программе. Структурированные данные, виды и способы их передачи. Передача структурированных данных с сосредоточенными и распределенными элементами. Примеры.
- •Коллективные передачи данных в mpi. Виды коллективных операций. Барьерная синхронизация, распределение и сбор данных. Примеры.
- •Коллективные передачи данных в mpi. Виды коллективных операций. Рассылка данных и их редукция. Примеры.
- •1. Классификация Флина.
- •2. Классификация Хокни
- •3. Классификация по доступу к памяти
- •По степени интеграции ядер и устройств
- •2. По однородности ядер
- •3. По связям между ядрами
- •По степени интеграции ядер и устройств
- •2. По однородности ядер
- •3. По связям между ядрами
- •29. Сети и структуры сетей системы ibm Blue Gene/l. Конфигурация торовых колец. Монтаж на Midplane. Внешние сети и их подключение. Link Card и Service Card.
- •Объединённый модуль (Connected Unit)
- •Кластер Roadrunner
Стандарт mpi. Идентификация в mpi. Цели разработчиков mpi. Классификация функций mpi. Структура mpi-программы. Пример программы.
Стандарт MPI.
MPI – Message Passing Interface – интерфейс приема сообщений.
MPI – стандарт на программный инструментарий для обеспечения связей между ветвями параллельной программы. Стандарт MPI определяет синтаксис, семантику библиотечных функций, используемых при написании параллельных программ с передачей сообщений на языках Fortran, C, C++. MPI предоставляет единый механизм взаимодействия ветвей параллельной программы, независимо от машинной архитектуры, расположения ветвей по процессорам и API операционных систем. MPI поддерживает архитектуры: одно- и многопроцессорные. С общей и распределенной памятью. Программы, использующие MPI, легче отлаживаются и быстрее переносятся на другие платформы, в идеале просто перекомпиляцией.
Идентификация в MPI
Три типа идентификаторов:
1)Идентификаторов типов данных. Пример: MPI_Status
2)Определенные константы. Эти константы, которые могут вводиться в виде имени. В разных версиях MPI могут быть разные числа. Идентификаторы переопределенных констант всегда пишутся с заглавной буквы. Пример:
MPI_COMM_WORLD, MPI_INIT
3)Идентификаторы функций библиотеки. Строятся по двум шаблонам:
- MPI_<имя категории функции>_<имя операции>(MPI_Comm_size)
- MPI_<имя операции> (MPI_Send)
<имя категории функции> - первая буквы заглавная, остальные маленькие
<имя операции> - всегда маленькими
Цели MPI (разработка)
Разработать программный интерфейс параллельного программирования
Достичь эффективности коммуникаций
Возможность выполнять параллельные программы в разнородной среде.
Использование стилей Fortran и C интерфейса
Достижение надежности коммуникаций
Определить интерфейс, который не отличался от уже используемых и обеспечить этот интерфейс расширениями для достижения гибкости
Определить интерфейс, который можно было бы реализовать на многих существующих системах.
Классификация функций MPI
1)Локальность:
- локальные - вызываются и действуют только в рамках одного вызывающего их процесса
(MPI_Comm_size, MPI_Comm_rank)
- не локальные - по своему действию связаны с функциями других процессов (MPI_Send)
- общие – для выполнения своей задачи должны быть вызваны всеми процессам программы или области связи (MPI_init, MPI_Finalize, MPI_Barrier)
2) Синхронизация:
- синхронные
- асинхронные
3) Блокировка:
- блокирующие (приостанавливают на время выполнения своей работы вызывающий их процесс)
- не блокирующие (возвращает управление процессу сразу после их вызова, выполняет свои действия в фоновом режиме)
4) Передача данных
- передающие (участвуют в передаче данных)
- не передающие
Структура MPI программы.
1) Включение библиотеки MPI
#include “mpi.h”
2) Часть программы до инициализации MPI. Здесь нельзя вызывать функции MPI, кроме функции:
Int MPI_Initialize(int *flag) - возвращает код ошибки, в качестве параметра возвращает адрес
Пример:
int a;
MPI_Initialized(&a);
С помощью этой функции процесс проверяет, была ли проведена инициализация MPI или еще нет.
Инициализация MPI производиться путем вызова функции:
int MPI_Init(int *argc, char ***argv);
argc – рассылка числа параметров командной строки
argv – рассылка массива параметров
3) Работа с MPI функциями. Оп окончании вызывается функция
int MPI_Finalize();
Она завершает работу программы с MPI.
4) Часть программы, где работают с функциями MPI вызывать MPI_Finalize() нельзя, но можно
int MPI_Finalized(int *flag);
Она аналогична проверяет была ли выполнена команда MPI_Finalize или нет.
Пример программы.
#include <stdio.h>
#include “mpi.h”
#define N 16
int main (int *argc, char **argv)
{ int rank, size, a, I, M[N];
MPI_Status;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&size); //кол-во загружаемых процессов
MPI_Comm_rank(MPI_COMM_WORLD,&rank); //номера загружаемых процессов
a=3;
for (i=rank*N/size; i<(rank+1)*N/size; i++)
M[i]=a+i;
if (rank==0)
MPI_Recv(&M[rank*N/size],N/size,MPI_INT,1,0,MPI_COMM_WORLD,&status);
if (rank==1)
MPI_Send(&M[rank*N/size],N/size,MPI_INT,0,0,MPI_COMM_WORLD);
MPI_Finalize();
return 0;
}
Стандарт MPI. Цели MPI. Операции поддерживаемые и неподдерживаемые в MPI. Идентификация в MPI. Двухточечные передачи в MPI (виды передач и функции для посылок и приемов сообщений). Примеры организации передач.
Стандарт MPI.
MPI – Message Passing Interface – интерфейс приема сообщений.
MPI – стандарт на программный инструментарий для обеспечения связей между ветвями параллельной программы. Стандарт MPI определяет синтаксис, семантику библиотечных функций, используемых при написании параллельных программ с передачей сообщений на языках Fortran, C, C++. MPI предоставляет единый механизм взаимодействия ветвей параллельной программы, независимо от машинной архитектуры, расположения ветвей по процессорам и API операционных систем. MPI поддерживает архитектуры: одно- и многопроцессорные. С общей и распределенной памятью. Программы, использующие MPI, легче отлаживаются и быстрее переносятся на другие платформы, в идеале просто перекомпиляцией.
Операции, поддерживаемые в MPI:
1.Операции взаимодействия процесс-процесс (двухточечная передача) MPI_Send
2.Коллективные операции. MPI_Bast – пересылка от одного ко всем.
3.Операции над группами процессов (пример операции над группами: создание, пересечение, сравнение и т.д.).
4.Операции над областями связи.
5.Операции над топологиями процессов.
6.Операции с типами данных.
7. Вспомогательная синхронизация и замеры времени.
Комментарий в лекции не было. Топология используется программистом для более удобного обозначения процессов, и таким образом, приближения параллельной программы к структуре математического алгоритма. Кроме того, топология может использоваться системой для оптимизации распределения процессов по физическим процессорам используемого параллельного компьютера при помощи изменения порядка нумерации процессов внутри коммуникатора. В MPI предусмотрены два типа топологий:
Операции не поддерживаемые в MPI:
1) Явные операции с разделяемой памятью.
Средства конструирования программ.
Средства отладки.
Операции с большей поддержкой ОС.
Функции ввода-вывода на периферию.
Идентификация в MPI
Три типа идентификаторов:
1)Идентификаторы типов данных. Пример: MPI_Status
2)Определенные константы. Эти константы, которые могут вводиться в виде имени. В разных версиях MPI могут быть разные числа. Идентификаторы переопределенных констант всегда пишутся с заглавной буквы. Пример:
MPI_COMM_WORLD, MPI_INIT
3)Идентификаторы функций библиотеки. Строятся по двум шаблонам:
- MPI_<имя категории функции>_<имя операции>(MPI_Comm_size)
- MPI_<имя операции> (MPI_Send)
<имя категории функции> - первая буквы заглавная, остальные маленькие
<имя операции> - всегда маленькими
Двухточечные передачи в MPI
Двухточечные передачи состоят из двух частей – посылки сообщения и приема.
Передачи могут выполняться в трех режимах:
- блокирующем;
- неблокирующем (процесс, вызвавший функцию передачи продолжает выполняться, а процедура приема-передачи идет в фоновом режиме);
- отложенном.
Функции с блокирующей посылкой и блокирующим приемом данных:
Обычная асинхронная посылка
Int MPI_Send(void *buf, int count MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm);
buf – адрес начала передаваемого блока данных; datatype – тип посылаемых элементов
dest – номер процесса получателя; msgtag – тег сообщения; comm – идентификатор (id) области связи, в которой осуществляется передача.
Эта функция блокирует вызывающий ее процесс до тех пор пока не считает все данные из памяти и не передаст их в какой либо системный буфер.
Блокирующий прием данных
Int MPI_Recv(void *buf, int count MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Status *status);
Status – переменная структура с параметрами принимаемого сообщения (Он ссылается на запись с двумя полями: одно - для источника, другое - для тэга. Например, если в качестве источника был указан MPI_ANY_SOURCE, то status будет содержать ранг процесса, который прислал сообщение)
Функция блокирует вызывающий ее процесс до тех пор пока данные не будут получены в полном объеме и полностью сохранены в памяти принимающего процесса.
Тэг при приеме/передаче должен совпадать. Если неизвестно от какого процесса будет приниматься сообщение, то ставиться вместо source константа MPI_ANY_SOURSE, вместо тэга – MPI_ANY_TAG.
Используя MPI_Status, можно получить статус сообщения: номер отправителя, id сообщения, код ошибки.
Int MPI_Bsend(<аналогично MPI_Send>);
Блокирующая асинхронная буферизированная посылка. В отличии от MPI_Send создается принудительный буфер, куда скидываются данные из памяти процесса. После скидывания происходит разблокирование процесса.
Int MPI_Ssend(<аналогично MPI_Send>);
Функция блокирующей синхронной посылки. Процесс, вызывающий эту функцию, блокируется до тех пор, пока посылаемое сообщение не будет полностью принято процессом получателем.
Int MPI_Rsend(<аналогично MPI_Send>);
Блокирующая согласованная посылка. Посылающий процесс блокируется до тех пор, пока принимающий процесс не готов принимать сообщение. Не все реализации MPI поддерживают эту функцию.
Int MPI_Sendrecv(void *sbuf, int scount MPI_Datatype stype, int dest, int stag, void *rbuf, int rcount MPI_Datatype rtype, int source, int rtag, MPI_Comm comm, MPI_Status *status);
Эта функция одновременно посылает 1 сообщение и принимает другое. MPI решает что делать сначала: принимать или послать.
sbuf – адрес в памяти, откуда считываются данные для посылки; scount – число элементов в посылаемом сообщении; datatype – тип посылаемых элементов; dest – номер процесса получателя; stag – тег сообщения (номер посылаемого сообщения). Вторая половина аналогична, для приема данных. comm – идентификатор (id) области связи, в которой осуществляется передача.
Status – статус принимаемого сообщения.
Вспомогательные функции:
Int MPI_Probe(int source, int msgtag, MPI_Comm comm, MPI_Status *status);
Позволяет получить информацию об интересующем сообщении. Процесс вызывающий ее блокируется до тех пор, пока не получит сообщение с указанными параметрами. Функция реально не принимает сообщение, а просто блокирует процесс.
Int MPI_Iprobe(int source, int msgtag, int *flag, MPI_Comm comm, MPI_Status *status);
Это не блокирующая функция. Возвращает flag = true, если имеется сообщение, которое может быть получено и которое соответствует образцу, описанному аргументами source, tag, и comm.
Не блокирующие передачи данных в MPI
Int MPI_Isend(<аналогично MPI_Send>, MPI_Request *request);
Не блокирующая асинхронная посылка. Позволяет сократить время, т.к. нет блокировки все в фоновом режиме; но данные могут быть искажены процессом.
request – переменная идентифицирующая не блокирующие процедуры.
Int MPI_Ibsend(<аналогично MPI_Isend>);
Int MPI_Issend(<аналогично MPI_Isend>);
Int MPI_Irsend(<аналогично MPI_Isend>);
Int MPI_Irecv(<аналогично MPI_Recv, кроме status его нет!>, MPI_Request *request);
На момент выхода из функции MPI_Irecv как правило никакого сообщения еще не получено.
Int MPI_Isendrecv(…);
Если процессу есть чем заняться, тогда эта функция более эффективна.
Вспомогательные функции
- Функции семейства MPI_Wait
int MPI_Wait(MPI_Request *request, MPI_Status *status);
Блокирует вызывающий ее процесс то тех пор пока не закончиться не блокирующая процедура идентифицируемая параметром request. По окончании процедуры происходит разблокировка процесса, а в параметре status возвращается статус завершенной процедуры приема.
Int MPI_Waitall(int count,MPI_Request *request, MPI_Status *status);
Блокирует вызывающий ее процесс, пока не завершатся все процедуры, указанные в массиве request. Число этих процедур задается count.
Int MPI_Waitany(int count,MPI_Request *request,int *index, MPI_Status *status);
Блокирует вызывающий ее процесс до завершения любой из процедур, указанных в параметре массиве request. Число этих процедур задается параметром count. index – номер завершившейся процедуры.
Int MPI_Waitsome(int incount, MPI_Request *request, int outcount, int *indexes, MPI_Status *status);
Аналог MPI_Waitany, только в случае завершения нескольких процессов она возвращает общее число outcount и их номера в массиве indexes
- Функции семейства MPI_Test
Позволяет вызывающему их процессу проверить завершились ли интересующие не блокирующие процедуры. В отличии от MPI_Wait эти функции не блокируют процесс, а только извещают с помощью параметра flag о факте завершения процедуры.
Int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);
Int MPI_Testall(int count, MPI_Request *request, int *flag, MPI_Status *status);
Int MPI_Testany(int count, MPI_Request *request, int *index, int *flag, MPI_Status *status);
Int MPI_Testsome(int incount, MPI_Request *request, int outcount, int *indexes, int *flag, MPI_Status *status);
Отложенные двухточечные передачи данных в MPI.
Протокол работы процесса с отложенной передачей разделяется на три этапа:
1) Инициализация отложенной процедуры (посылки или приема)
В ходе этого этапа вызывается функция инициализации и осуществляется настройка среды для последующей посылки или приема.
2) Запуск отложенной процедуры.
3) Проверка или ожидание завершения процедуры.
Отложенные процедуры передачи являются не блокирующими.
Int MPI_Send_init(void *buf, int count MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request);
Int MPI_Bsend_init(void *buf, int count MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request);
Int MPI_Ssend_init(void *buf, int count MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request);
Int MPI_Rsend_init(void *buf, int count MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request);
Int MPI_Recv_init(void *buf, int count MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Request *request);
Функции запуска отложенной процедуры, идентифицируемой параметром request
Int MPI_Start(MPI_Request *request);
Int MPI_Startall(int count, MPI_Request *request);
В параметр count записывается размер массива request.
Пример использования функций неблокирующих передач. (придумывал сам, так что не обессудьте:)))
X=A*B+C*D+K*T
Y=A*B+C*D+Z*F
#include <stdio.h>
#include "mpi.h"
#include "func.h"
int main(int argc, char ** argv)
{ int size,rank;
float A[N][N], B[N][N], C[N][N], D[N][N], K[N][N], T[N][N], Z[N][N], F[N][N], X[N][N], Y[N][N];
MPI_Request request1, request2;
MPI_Status status1, status2;
inits(A,B,C,D,K,T,Z,F);
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&size); //size=число процессов
MPI_Comm_rank(MPI_COMM_WORLD,&rank); //rank=номер процесса
if (rank ==0)
{ //вычисление общего слагаемого А*В
sub(A,B);
//передача общего слагаемого А*В
MPI_Isend(A,N*N,MPI_FLOAT,1,0,MPI_COMM_WORLD,&request1);
//прием общего слогаемого С*D
MPI_Irecv(C,N*N,MPI_FLOAT,1,0,MPI_COMM_WORLD,&request2);
//расчет локального слогаемого K*T
sub(K,T);
//Ожидание передаи и приема все слогаемых
MPI_Wait(&request1,&status1);
MPI_Wait(&request2,&status2);
//Окончательный расчет
Add(A,C,K,X);
//Вывод результата
Out(X);
}
if (rank ==1)
{
//вычисление общего слагаемого А*В
sub(С,D);
//передача общего слагаемого C*D
MPI_Isend(C,N*N,MPI_FLOAT,0,0,MPI_COMM_WORLD,&request1);
//прием общего слогаемого A*B
MPI_Irecv(A,N*N,MPI_FLOAT,0,0,MPI_COMM_WORLD,&request2);
//расчет локального слогаемого K*T
sub(Z,F);
//Ожидание передаи и приема все слогаемых
MPI_Wait(&request1,&status1);
MPI_Wait(&request2,&status2);
//Окончательный расчет
Add(A,C,Z,Y);
//Вывод результата
Out(Y);
}
MPI_Finalize(); return 0; }
Двухточечные передачи в MPI. Организация блокирующих и неблокирующих посылок/приемов сообщений. Функции (основные и вспомогательные), используемые при неблокирующих посылках и приемах сообщений. Примеры.
Двухточечные передачи в MPI
Двухточечные передачи состоят из двух частей – посылки сообщения и приема.
Передачи могут выполняться в трех режимах:
- блокирующем;
- не блокирующем (процесс, вызвавший функцию передачи продолжает выполняться, а процедура приема-передачи идет в фоновом режиме);
- отложенном.
Функции с блокирующей посылкой и блокирующим приемом данных:
Обычная асинхронная посылка
Int MPI_Send(void *buf, int count MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm);
buf – адрес начала передаваемого блока данных; datatype – тип посылаемых элементов;
dest – номер процесса получателя; msgtag – тег сообщения; comm. – идентификатор (id) области связи, в которой осуществляется передача
Эта функция блокирует вызывающий ее процесс до тех пор пока не считает все данные из
памяти и не передаст их в какой либо системный буфер.
Блокирующий прием данных
Int MPI_Recv(void *buf, int count MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm,
MPI_Status *status);
Status – переменная структура с параметрами принимаемого сообщения (Он ссылается на запись
с двумя полями: одно - для источника, другое - для тэга. Например, если в качестве источника
был указан MPI_ANY_SOURCE, то status будет содержать ранг процесса, который прислал
сообщение)
Функция блокирует вызывающий ее процесс до тех пор пока дыне на будут получены в полном
объеме и полностью сохранены в памяти принимающего процесса.
Тэг при приеме/передаче должен совпадать. Если неизвестно от какого процесса будет
приниматься сообщение, то ставиться вместо source константа MPI_ANY_SOURSE, вместо тэга –
MPI_ANY_TAG.
Используя MPI_Status, можно получить статус сообщения: номер отправителя, id сообщения, код
ошибки.
Int MPI_Bsend(<аналогично MPI_Send>);
Блокирующая асинхронная буферизированная посылка. В отличии от MPI_Send создается
принудительный буфер, куда скидываются данные из памяти процесса. После скидывания
происходит разблокирование процесса.
Int MPI_Ssend(<аналогично MPI_Send>);
Функция блокирующей синхронной посылки. Процесс, вызывающий эту функцию, блокируется
до тех пор, пока посылаемое сообщение не будет полностью принято процессом получателем.
Int MPI_Rsend(<аналогично MPI_Send>);
Блокирующая согласованная посылка. Посылающий процесс блокируется до тех пор, пока
принимающий процесс не готов принимать сообщение. Не все реализации MPI поддерживают эту
функцию.
Int MPI_Sendrecv(void *sbuf, int scount MPI_Datatype stype, int dest, int stag, void *rbuf, int rcount MPI_Datatype rtype, int source, int rtag, MPI_Comm comm, MPI_Status *status);
Эта функция одновременно посылает 1 сообщение и принимает другое. MPI речает что делать сначала: принимать или послать.
sbuf – адрес в памяти, откуда считываются данные для посылки; scount – число элементов в посылаемом сообщении; datatype – тип посылаемых элементов; dest – номер процесса получателя
stag – тег сообщения (номер посылаемого сообщения). Вторая половина аналогична, для приема данных. comm – идентификатор (id) области связи, в которой осуществляется передача.
Status – статус принимаемого сообщения. Вспомогательные функции:
Int MPI_Probe(int source, int msgtag, MPI_Comm comm, MPI_Status *status);
Позволяет получить информацию об интересующем сообщении. Процесс вызывающий ее блокируется до тех пор, пока не получит сообщение с указанными параметрами. Функция реально не принимает сообщение, а просто блокирует процесс.
Int MPI_Iprobe(int source, int msgtag, int *flag, MPI_Comm comm, MPI_Status *status);
Это не блокирующая функция. Возвращает flag = true, если имеется сообщение, которое может быть получено и которое соответствует образцу, описанному аргументами source, tag, и comm.
Не блокирующие передачи данных в MPI
Int MPI_Isend(<аналогично MPI_Send>, MPI_Request *request);
Не блокирующая асинхронная посылка. Позволяет сократить время, т.к. нет блокировки все в фоновом режиме; но данные могут быть искажены процессом.
request – переменная идентифицирующая не блокирующие процедуры.
Int MPI_Ibsend(<аналогично MPI_Isend>);
Int MPI_Issend(<аналогично MPI_Isend>);
Int MPI_Irsend(<аналогично MPI_Isend>);
Int MPI_Irecv(<аналогично MPI_Recv, кроме status его нет!>, MPI_Request *request);
На момент выхода из функции MPI_Irecv как правило никакого сообщения еще не получено.
Int MPI_Isendrecv(…);
Если процессу есть чем заняться, тогда эта функция более эффективна.
Вспомогательные функции
- Функции семейства MPI_Wait
int MPI_Wait(MPI_Request *request, MPI_Status *status);
Блокирует вызывающий ее процесс то тех пор пока не закончиться не блокирующая процедура идентифицируемая параметром request. По окончании процедуры происходит разблокировка процесса, а в параметре status возвращается статус завершенной процедуры приема.
Int MPI_Waitall(int count,MPI_Request *request, MPI_Status *status);
Блокирует вызывающий ее процесс, пока не завершатся все процедуры, указанные в массиве request. Число этих процедур задается count.
Int MPI_Waitany(int count,MPI_Request *request,int *index, MPI_Status *status);
Блокирует вызывающий ее процесс до завершения любой из процедур, указанных в параметре массиве request. Число этих процедур задается параметром count. index – номер завершившейся процедуры.
Int MPI_Waitsome(int incount, MPI_Request *request, int outcount, int *indexes, MPI_Status *status);
Аналог MPI_Waitany, только в случае завершения нескольких процессов она возвращает общее число outcount и их номера в массиве indexes
- Функции семейства MPI_Test
Позволяет вызывающему их процессу проверить завершились ли интересующие не блокирующие процедуры. В отличии от MPI_Wait эти функции не блокируют процесс, а только извещают с помощью параметра flag о факте завершения процедуры.
Int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);
Int MPI_Testall(int count, MPI_Request *request, int *flag, MPI_Status *status);
Int MPI_Testany(int count, MPI_Request *request, int *index, int *flag, MPI_Status *status);
Int MPI_Testsome(int incount, MPI_Request *request, int outcount, int *indexes, int *flag, MPI_Status *status);
