Параллельные системы (мини-контрольная) Базовые понятия mpi


с. 1

Параллельные системы


(мини-контрольная)

Базовые понятия MPI


В вычислительных системах с распределенной памятью процессоры работают независимо друг от друга. Для организации параллельных вычислений в таких условиях необходимо иметь возможность распределять вычислительную нагрузку и организовать информационное взаимодействие (передачу данных) между процессорами.

Решение всех перечисленных вопросов и обеспечивает интерфейс передачи данных (message passing interface - MPI).

1. В общем плане, для распределения вычислений между процессорами необходимо проанализировать алгоритм решения задачи, выделить информационно независимые фрагменты вычислений, провести их программную реализацию и затем разместить полученные части программы на разных процессорах. В рамках MPI принят более простой подход – для решения поставленной задачи разрабатывается одна программа и эта единственная программа запускается одновременно на выполнение на всех имеющихся процессорах! При этом для того, чтобы избежать идентичности вычислений на разных процессорах, можно, во-первых, подставлять разные данные для программы на разных процессорах, а во-вторых, в MPI имеются средства для идентификации процессора, на котором выполняется программа (и тем самым, предоставляется возможность организовать различия в вычислениях в зависимости от используемого программой процессора).

Подобный способ организации параллельных вычислений получил наименование модели "одна программа множество процессов" (single program multiple processes ).

2. Для организации информационного взаимодействия между процессорами в самом минимальном варианте достаточно операции приема и передачи данных (при этом, конечно, должна существовать техническая возможность коммуникации между процессорами – каналы или линии связи). В MPI существует целое множество операций передачи данных.

MPI позволяет в значительной степени снизить остроту проблемы переносимости параллельных программ между разными компьютерными системами – параллельная программа, разработанная на алгоритмическом языке C или Fortran с использованием библиотеки MPI, как правило, будет работать на разных вычислительных платформах. MPI содействует повышению эффективности параллельных вычислений, поскольку в настоящее время практически для каждого типа вычислительных систем существуют реализации библиотек MPI, в максимальной степени учитывающие возможности используемого компьютерного оборудования.MPI уменьшает, в определенном плане, сложность разработки параллельных программ.


Понятие параллельной программы.


Под параллельной программой в рамках MPI понимается множество одновременно выполняемых процессов. Процессы могут выполняться на разных процессорах, но на одном процессоре могут располагаться и несколько процессов (в этом случае их исполнение осуществляется в режиме разделения времени). В предельном случае для выполнения параллельной программы может использоваться один процессор – как правило, такой способ применяется для начальной проверки правильности параллельной программы.

Каждый процесс параллельной программы порождается на основе копии одного и того же программного кода (модель SPMP). Данный программный код, представленный в виде исполняемой программы, должен быть доступен в момент запуска параллельной программы на всех используемых процессорах. Исходный программный код для исполняемой программы разрабатывается на алгоритмических языках C или Fortran с использованием той или иной реализации библиотеки MPI.

Количество процессов и число используемых процессоров определяется в момент запуска параллельной программы средствами среды исполнения MPI-программ.

Операции передачи данных.


Основу MPI составляют операции передачи сообщений. Среди предусмотренных в составе MPI функций различаются парные (point-to-point) операции между двумя процессами и коллективные (collective) коммуникационные действия для одновременного взаимодействия нескольких процессов.

Стандарт MPI предусматривает необходимость реализации большинства основных коллективных операций передачи данных.


Типы данных.


При выполнении операций передачи сообщений для указания передаваемых или получаемых данных в функциях MPI необходимо указывать тип пересылаемых данных. MPI содержит большой набор базовых типов данных, во многом совпадающих с типами данных в алгоритмических языках C и Fortran. Кроме того, в MPI имеются возможности для создания новых производных типов данных для более точного и краткого описания содержимого пересылаемых сообщений.

Введение в разработку параллельных программ с использованием MPI.


Инициализация и завершение MPI программ.

Первой вызываемой функцией MPI должна быть функция:

int MPI_Init ( int *agrc, char ***argv );

для инициализации среды выполнения MPI-программы. Параметрами функции являются количество аргументов в командной строке и текст самой командной строки.

Последней вызываемой функцией MPI обязательно должна являться функция:

int MPI_Finalize (void);

Как результат, можно отметить, что структура параллельной программы, разработанная с использованием MPI, должна иметь следующий вид:

#include "mpi.h"

int main ( int argc, char *argv[] ) {

<программный код без использования MPI функций>

MPI_Init ( &agrc, &argv );



<программный код с использованием MPI функций>

MPI_Finalize();



<программный код без использованием MPI функций>

return 0;

}

Следует отметить:



1. Файл mpi.h содержит определения именованных констант, прототипов функций и типов данных библиотеки MPI,

2. Функции MPI_Init и MPI_Finalize являются обязательными и должны быть выполнены (и только один раз) каждым процессом параллельной программы,

3. Перед вызовом MPI_Init может быть использована функция MPI_Initialized для определения того, был ли ранее выполнен вызов MPI_Init.

Рассмотренные примеры функций дают представление синтаксиса именования функций в MPI. Имени функции предшествует префикс MPI, далее следует одно или несколько слов названия, первое слово в имени функции начинается с заглавного символа, слова разделяются знаком подчеркивания. Названия функций MPI, как правило, поясняют назначение выполняемых функцией действий.


Определение количества и ранга процессов.


Определение количества процессов в выполняемой параллельной программе осуществляется при помощи функции:

int MPI_Comm_size ( MPI_Comm comm, int *size ).

Для определения ранга процесса используется функция:

int MPI_Comm_rank ( MPI_Comm comm, int *rank ).

Как правило, вызов функций MPI_Comm_size и MPI_Comm_rank выполняется сразу после

MPI_Init:

#include "mpi.h"

int main ( int argc, char *argv[] ) {

int ProcNum, ProcRank;

<программный код без использования MPI функций>

MPI_Init ( &agrc, &argv );

MPI_Comm_size ( MPI_COMM_WORLD, &ProcNum);

MPI_Comm_rank ( MPI_COMM_WORLD, &ProcRank);



<программный код с использованием MPI функций>

MPI_Finalize();



<программный код с использованием MPI функций>

return 0; }

Следует отметить:

1.Коммуникатор MPI_COMM_WORLD, как отмечалось ранее, создается по умолчанию и представляет все процессы выполняемой параллельной программы,

2.Ранг, получаемый при помощи функции MPI_Comm_rank, является рангом процесса, выполнившего вызов этой функции, т.е. переменная ProcRank будет принимать различные значения в разных процессах.

Передача сообщений.


Для передачи сообщения процесс-отправитель должен выполнить функцию:

int MPI_Send(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm),

где


  • buf – адрес буфера памяти, в котором располагаются данные отправляемого сообщения,

  • count – количество элементов данных в сообщении,

  • type - тип элементов данных пересылаемого сообщения,

  • dest - ранг процесса, которому отправляется сообщение,

  • tag - значение-тег, используемое для идентификации сообщений,

  • comm - коммуникатор, в рамках которого выполняется передача данных.

Прием сообщений.


Для приема сообщения процесс-получатель должен выполнить функцию:

int MPI_Recv(void *buf, int count, MPI_Datatype type, int source,

int tag, MPI_Comm comm, MPI_Status *status),

где


  • buf, count, type – буфер памяти для приема сообщения, назначение каждого отдельного параметра соответствует описанию в MPI_Send,

  • source - ранг процесса, от которого должен быть выполнен прием сообщения,

  • tag - тег сообщения, которое должно быть принято для процесса,

  • comm - коммуникатор, в рамках которого выполняется передача данных,

  • status – указатель на структуру данных с информацией о результате выполнения операции приема данных.

Следует отметить:

1. Буфер памяти должен быть достаточным для приема сообщения, а тип элементов передаваемого и принимаемого сообщения должны совпадать; при нехватке памяти часть сообщения будет потеряна и в коде завершения функции будет зафиксирована ошибка переполнения,

2. При необходимости приема сообщения от любого процесса-отправителя для параметра source может быть указано значение MPI_ANY_SOURCE,

3. При необходимости приема сообщения с любым тегом для параметра tag может быть указано значение MPI_ANY_TAG,

4. Параметр status позволяет определить ряд характеристик принятого сообщения:


  • status.MPI_SOURCE – ранг процесса-отправителя принятого сообщения,

  • status.MPI_TAG - тег принятого сообщения.

Функция MPI_Get_count(MPI_Status *status, MPI_Datatype type, int *count)возвращает в переменной count количество элементов типа type в принятом сообщении. Вызов функции MPI_Recv не должен согласовываться со временем вызова соответствующей функции передачи сообщения MPI_Send – прием сообщения может быть инициирован до момента, в момент или после момента начала отправки сообщения. По завершении функции MPI_Recv в заданном буфере памяти будет располагаться принятое сообщение. Принципиальный момент здесь состоит в том, что функция MPI_Recv является блокирующей для процесса-получателя, т.е. его выполнение приостанавливается до завершения работы функции. Таким образом, если по каким-то причинам ожидаемое для приема сообщение будет отсутствовать, выполнение параллельной программы будет блокировано.

Первая параллельная программа с использованием MPI.


Рассмотренный набор функций оказывается достаточным для разработки параллельных программ. Приводимая ниже программа является стандартным начальным примером для алгоритмического языка C.

#include

#include "mpi.h"

int main(int argc, char* argv[]){

int ProcNum, ProcRank, RecvRank;

MPI_Status Status;

MPI_Init(&argc, &argv);

MPI_Comm_size(MPI_COMM_WORLD, &ProcNum);

MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank);

if ( ProcRank == 0 ){



// Действия, выполняемые только процессом с рангом 0

printf ("\n Hello from process %3d", ProcRank);

for ( int i=1; i

MPI_Recv(&RecvRank, 1, MPI_INT, MPI_ANY_SOURCE,

MPI_ANY_TAG, MPI_COMM_WORLD, &Status);



printf("\n Hello from process %3d", RecvRank);

}

}



else // Сообщение, отправляемое всеми процессами,

// кроме процесса с рангом 0

MPI_Send(&ProcRank,1,MPI_INT,0,0,MPI_COMM_WORLD);

MPI_Finalize();

return 0;

}

Достижение эффективного выполнения операции передачи данных от одного процесса всем процессам программы (широковещательная рассылка данных) может быть обеспечено при помощи функции MPI:



int MPI_Bcast(void *buf,int count,MPI_Datatype type,int root,MPI_Comm comm),

где


  • buf, count, type – буфер памяти с отправляемым сообщением (для процесса с рангом 0), и для приема сообщений для всех остальных процессов,

  • root - ранг процесса, выполняющего рассылку данных,

  • comm - коммуникатор, в рамках которого выполняется передача данных.

Для наилучшего выполнения действий, связанных с редукцией данных(получение данных от всех потоков), в MPI предусмотрена функция:

int MPI_Reduce(void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,int root,MPI_Comm comm),


Синхронизация вычислений.


В ряде ситуаций независимо выполняемые в процессах вычисления необходимо синхронизировать. Так, например, для измерения времени начала работы параллельной программы необходимо, чтобы для всех процессов одновременно были завершены все подготовительные действия, перед окончанием работы программы все процессы должны завершить свои вычисления и т.п. Синхронизация процессов, т.е. одновременное достижение процессами тех или иных точек процесса вычислений, обеспечивается при помощи функции MPI:

int MPI_Barrier(MPI_Comm comm);

Функция MPI_Barrier определяет коллективную операции и, тем самым, при использовании должна вызываться всеми процессами используемого коммуникатора. При вызове функции MPI_Barrier выполнение процесса блокируется, продолжение вычислений процесса произойдет только после вызова функции MPI_Barrier всеми процессами коммуникатора.

Режимы передачи данных.


Для именования функций отправки сообщения для разных режимов выполнения в MPI используется название функции MPI_Send, к которому как префикс добавляется начальный символ названия соответствующего режима работы, т.е.

  • MPI_Ssend – функция отправки сообщения в синхронном режиме,

  • MPI_Bsend – функция отправки сообщения в буферизованном режиме,

  • MPI_Rsend – функция отправки сообщения в режиме по готовности.

Далее рассмотрим базовые коллективные операции передачи данных.

Обобщенная передача данных от одного процесса всем процессам.


Обобщенная операция передачи данных от одного процесса всем процессам (распределение данных) отличается от широковещательной рассылки тем, что процесс передает процессам различающиеся данные . Выполнение данной операции может быть обеспечено при помощи функции:

int MPI_Scatter(void *sbuf,int scount,MPI_Datatype stype,

void *rbuf,int rcount,MPI_Datatype rtype, int root, MPI_Comm comm),

где


  • sbuf, scount, stype - параметры передаваемого сообщения (scount определяет количесво элементов,передаваемых на каждый процесс),

  • rbuf, rcount, rtype - параметры сообщения, принимаемого в процессах,

  • root – ранг процесса, выполняющего рассылку данных,

  • comm - коммуникатор, в рамках которого выполняется передача данных.



Рис. 6. Общая схема операции обобщенной передачи данных от одного процесса всем процессам

Обобщенная передача данных от всех процессоров одному процессу.


Операция обобщенной передачи данных от всех процессоров одному процессу (сбор данных) является обратной к процедуре распределения данных (см. рис. 4.5). Для выполнения этой операции в MPI предназначена функция:

int MPI_Gather(void *sbuf,int scount,MPI_Datatype stype,

void *rbuf,int rcount,MPI_Datatype rtype,

int root, MPI_Comm comm),

где


  • sbuf, scount, stype - параметры передаваемого сообщения,

  • rbuf, rcount, rtype - параметры принимаемого сообщения,

  • root – ранг процесса, выполняющего сбор данных,

  • comm - коммуникатор, в рамках которого выполняется передача данных.



Рис. 7. Общая схема операции обобщенной передачи данных от всех процессов одному процессу

При выполнении функции MPI_Gather каждый процесс в коммуникаторе передает данные из буфера sbuf на процесс с рангом root. Процесс с рангом root собирает все получаемые данные в буфере rbuf (размещение данных в буфере осуществляется в соответствии с рангами процессов-отправителей сообщений). Для того, чтобы разместить все поступающие данные, размер буфера rbuf должен быть равен scount * p элементов, где p есть количество процессов в коммуникаторе comm. Функция MPI_Gather также определяет коллективную операцию, и ее вызов при выполнении сбора данных должен быть обеспечен в каждом процессе коммуникатора.


Общая передача данных от всех процессов всем процессам .


Передача данных от всех процессов всем процессам является наиболее общей операцией передачи данных . Выполнение данной операции может быть обеспечено при помощи функции:

int MPI_Alltoall(void *sbuf,int scount,MPI_Datatype stype,

void *rbuf,int rcount,MPI_Datatype rtype,MPI_Comm comm),

где


  • sbuf, scount, stype - параметры передаваемых сообщений,

  • rbuf, rcount, rtype - параметры принимаемых сообщений,

  • comm - коммуникатор, в рамках которого выполняется передача данных.



Рис. 8. Общая схема операции передачи данных от всех процессов всем процессам (сообщения показываются обозначениями вида ij, где i и j есть ранги передающих и принимающих процессов соответственно)

При выполнении функции MPI_Alltoall каждый процесс в коммуникаторе передает данные из scount элементов каждому процессу (общий размер отправляемых сообщений в процессах должен быть равен scount * p элементов, где p есть количество процессов в коммуникаторе comm) и принимает сообщения от каждого процесса.

Вызов функции MPI_Alltoall при выполнении операции общего обмена данными должен быть выполнен в каждом процессе коммуникатора. Вариант операции общего обмена данных, когда размеры передаваемых процессами сообщений могут быть различны, обеспечивается при помощи функций MPI_Alltoallv.

Производные типы данных в MPI.


Для обеспечения бóльших возможностей при определении состава передаваемых сообщений в MPI предусмотрен механизм так называемых производных типов данных.

В самом общем виде под производным типом данных в MPI можно понимать описание набора значений предусмотренного в MPI типа, причем в общем случае описываемые значения не обязательно непрерывно располагаются в памяти. Задание типа в MPI принято осуществлять при помощи карты типа (type map) в виде последовательности описаний входящих в тип значений, каждое отдельное значение описывается указанием типа и смещения адреса месторасположения от некоторого базового адреса, т.е.



TypeMap =[(type0,disp0),...,(typen − 1,dispn − 1].

Часть карты типа с указанием только типов значений именуется в MPI сигнатурой типа:



TypeSignature = [(type0,..,typen − 1)].

Сигнатура типа описывает, какие базовые типы данных образуют некоторый производный тип данных MPI и, тем самым, управляет интерпретацией элементов данных при передаче или получении сообщений. Смещения карты типа определяют, где находятся значения данных.


Управление коммуникаторами.


Сейчас мы рассмотрим управление интракоммуникаторами, используемыми для операций передачи данных внутри одной группы процессов.

Для создания новых коммуникаторов применимы два основных способа их получения: дублирование уже существующего коммуникатора:

int MPI_Comm_dup ( MPI_Comm oldcom, MPI_comm *newcomm),

создание нового коммуникатора из подмножества процессов существующего коммуникатора:



int MPI_comm_create (MPI_Comm oldcom, MPI_Group group, MPI_Comm *newcomm).

Дублирование коммуникатора может использоваться, например, для устранения возможности пересечения по тегам сообщений в разных частях параллельной программы (в т.ч. и при использовании функций разных программных библиотек). Следует отметить также, что операция создания коммуникаторов является коллективной и, тем самы м, должна выполняться всеми процессами исходного коммуникатора.
с. 1

скачать файл

Смотрите также: