Для приема сообщения процесс -получатель должен выполнить функцию:
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 – указатель на структуру данных с информацией о результате выполнения операции приема данных.
- буфер памяти должен быть достаточным для приема сообщения. При нехватке памяти часть сообщения будет потеряна и в коде завершения функции будет зафиксирована ошибка переполнения; с другой стороны, принимаемое сообщение может быть и короче, чем размер приемного буфера, в таком случае изменятся только участки буфера, затронутые принятым сообщением;
- типы элементов передаваемого и принимаемого сообщения должны совпадать;
- при необходимости приема сообщения от любого процесса — отправителя для параметра source может быть указано значение MPI_ANY_SOURCE (в отличие от функции передачи MPI_Send , которая отсылает сообщение строго определенному адресату);
- при необходимости приема сообщения с любым тегом для параметра tag может быть указано значение MPI_ANY_TAG (опять-таки, при использовании функции MPI_Send должно быть указано конкретное значение тега);
- в отличие от параметров » процесс -получатель» и «тег», параметр «коммуникатор» не имеет значения, означающего «любой коммуникатор»;
- параметр status позволяет определить ряд характеристик принятого сообщения:
- status.MPI_SOURCE — ранг процесса – отправителя принятого сообщения;
- status.MPI_TAG — тег принятого сообщения.
Приведенные значения MPI_ANY_SOURCE и MPI_ANY_TAG иногда называют джокерами.
Лекция 2. Основные функции библиотеки MPI.
Значение переменной status позволяет определить количество элементов данных в принятом сообщении при помощи функции:
int MPI_Get_count(MPI_Status *status, MPI_Datatype type, int *count),
- status — статус операции MPI_Recv ;
- type — тип принятых данных;
- count — количество элементов данных в сообщении.
Вызов функции MPI_Recv не обязан быть согласованным со временем вызова соответствующей функции передачи сообщения MPI_Send – прием сообщения может быть инициирован до момента, в момент или после момента начала отправки сообщения.
По завершении функции MPI_Recv в заданном буфере памяти будет располагаться принятое сообщение. Принципиальный момент здесь состоит в том, что функция MPI_Recv является блокирующей для процесса -получателя, т.е. его выполнение приостанавливается до завершения работы функции. Таким образом, если по каким-то причинам ожидаемое для приема сообщение будет отсутствовать, выполнение параллельной программы будет блокировано.
Параллельные процессы MPI
5.2.1.5. Первая параллельная программа с использованием MPI
Рассмотренный набор функций оказывается достаточным для разработки параллельных программ 4 Как было обещано ранее, количество функций MPI , необходимых для начала разработки параллельных программ , оказалось равным шести. . Приводимая ниже программа является стандартным начальным примером для алгоритмического языка C .
Программа 5.1. Первая параллельная программа с использованием MPI
#include #include «mpi.h» int main(int argc, char* argv[]) < int ProcNum, ProcRank, RecvRank; MPI_Status Status; MPI_Init(argv); MPI_Comm_size(MPI_COMM_WORLD, MPI_Comm_rank(MPI_COMM_WORLD, if ( ProcRank == 0 )< // Действия, выполняемые только процессом с рангом 0 printf(«n Hello from process %3d», ProcRank); for (int i = 1; i < ProcNum; i++ ) < MPI_Recv(Status); printf(«n Hello from process %3d», RecvRank); >> else // Сообщение, отправляемое всеми процессами, // кроме процесса с рангом 0 MPI_Send( MPI_Finalize(); return 0; >
Как следует из текста программы, каждый процесс определяет свой ранг, после чего действия в программе разделяются. Все процессы , кроме процесса с рангом 0 , передают значение своего ранга нулевому процессу . Процесс с рангом 0 сначала печатает значение своего ранга, а далее последовательно принимает сообщения с рангами процессов и также печатает их значения. При этом важно отметить, что порядок приема сообщений заранее не определен и зависит от условий выполнения параллельной программы (более того, этот порядок может изменяться от запуска к запуску). Так, возможный вариант результатов печати процесса 0 может состоять в следующем (для параллельной программы из четырех процессов ):
Hello from process 0 Hello from process 2 Hello from process 1 Hello from process 3
Такой «плавающий» вид получаемых результатов существенным образом усложняет разработку, тестирование и отладку параллельных программ , т.к. в этом случае исчезает один из основных принципов программирования – повторяемость выполняемых вычислительных экспериментов. Как правило, если это не приводит к потере эффективности, следует обеспечивать однозначность расчетов и при использовании параллельных вычислений. Для рассматриваемого простого примера можно восстановить постоянство получаемых результатов при помощи задания ранга процесса -отправителя в операции приема сообщения:
MPI_Recv(Status).
Указание ранга процесса -отправителя регламентирует порядок приема сообщений, и, как результат, строки печати будут появляться строго в порядке возрастания рангов процессов (повторим, что такая регламентация в отдельных ситуациях может приводить к замедлению выполняемых параллельных вычислений).
Следует отметить еще один важный момент: разрабатываемая с применением MPI программа, как в данном частном варианте, так и в самом общем случае, используется для порождения всех процессов параллельной программы а значит, должна определять вычисления, выполняемые всеми этими процессами . Можно сказать, что MPI — программа является некоторой «макропрограммой», различные части которой используются разными процессами . Так, например, в приведенном примере программы выделенные рамкой участки программного кода не выполняются одновременно ни одним из процессов . Первый выделенный участок с функцией приема MPI_Recv исполняется только процессом с рангом 0 , второй участок с функцией передачи MPI_Send задействуется всеми процессами , за исключением нулевого процесса .
Для разделения фрагментов кода между процессами обычно используется подход, примененный в только что рассмотренной программе, – при помощи функции MPI_Comm_rank определяется ранг процесса , а затем в соответствии с рангом выделяются необходимые для процесса участки программного кода. Наличие в одной и той же программе фрагментов кода разных процессов также значительно усложняет понимание и, в целом, разработку MPI -программы. Как результат, можно рекомендовать при увеличении объема разрабатываемых программ выносить программный код разных процессов в отдельные программные модули (функции). Общая схема MPI -программы в этом случае будет иметь вид:
MPI_Comm_rank(MPI_COMM_WORLD, if ( ProcRank == 0 ) DoProcess0(); else if ( ProcRank == 1 ) DoProcess1(); else if ( ProcRank == 2 ) DoProcess2();
Во многих случаях, как и в рассмотренном примере, выполняемые действия являются отличающимися только для процесса с рангом 0 . В этом случае общая схема MPI -программы принимает более простой вид:
MPI_Comm_rank(MPI_COMM_WORLD, if ( ProcRank == 0 ) DoManagerProcess(); else DoWorkerProcesses();
В завершение обсуждения примера поясним примененный в MPI подход для контроля правильности выполнения функций. Все функции MPI (кроме MPI_Wtime и MPI_Wtick ) возвращают в качестве своего значения код завершения. При успешном выполнении функции возвращаемый код равен MPI_SUCCESS . Другие значения кода завершения свидетельствуют об обнаружении тех или иных ошибочных ситуаций в ходе выполнения функций. Для выяснения типа обнаруженной ошибки используются предопределенные именованные константы, среди которых:
- MPI_ERR_BUFFER — неправильный указатель на буфер;
- MPI_ERR_TRUNCATE — сообщение превышает размер приемного буфера;
- MPI_ERR_COMM — неправильный коммуникатор;
- MPI_ERR_RANK — неправильный ранг процесса и др.
Полный список констант для проверки кода завершения содержится в файле mpi.h . Однако, по умолчанию, возникновение любой ошибки во время выполнения функции MPI приводит к немедленному завершению параллельной программы . Для того чтобы иметь возможность проанализировать возвращаемый код завершения, необходимо воспользоваться предоставляемыми MPI функциями по созданию обработчиков ошибок и управлению ими, рассмотрение которых не входит в материал данной лекции.
Источник: intuit.ru
3. Примеры параллельных программ
Рассмотрим в качестве первого примера программу, выводящую на экран сообщение «Hello» от каждого из процессов.
#include «mpi.h»
#define BUF_LEN 256 /* длина буфера сообщений */
#define MSG_TAG 100 /* метка сообщений */
int main(int argc, char *argv[])
int my_rank; /* ранг текущего
int numprocs; /* общее число
int source; /* ранг отправителя */
int dest; /* ранг получателя */
char message[BUF_LEN];/* буфер для сообщения */
MPI_Status status; /* информация о полученном
/* Начать работу с MPI */
/* Получить номер текущего процесса
в группе всех процессов */
/* Получить общее количество
запущенных процессов */
/* Посылаем сообщения процессу 0,
который их выводит на экран */
if (my_rank != 0)
/* Создаем сообщение */
sprintf(message, «Hello from process %d!»,
/* Отправляем его процессу 0 */
MPI_Send(message, strlen(message) + 1,
MPI_CHAR, dest, MSG_TAG,
/* В процессе 0: получаем сообщение от
процессов 1. numprocs-1 и выводим
его на экран */
MPI_Recv(message, BUF_LEN, MPI_CHAR, source,
/* Заканчиваем работу с MPI */
Компиляция этого файла выполняется командой
$ mpicc MPI_simple.c -o MPI_simple
$ mpirun -np 2 MPI_simple .
Здесь символ $ означает приглашение операционной системы к вводу команды (его набирать не нужно!), опция -np задает количество параллельно работающих процессов (в данном случае 2). Попробуйте его изменить, чтобы выяснить, как это отражается на работе программы.
3.2. Вычисление интеграла
Рассмотрим в качестве примера задачу вычисления определенного интеграла от функции
по отрезку
. Для ускорения работы программы на вычислительной установке с
процессорами мы воспользуемся аддитивностью интеграла:

,

.
Использовав для приближенного определения каждого из слагаемых


этой суммы составную формулу трапеций и поручив эти вычисления своему процессору, мы получим –кратное ускорение работы. Простейшая программная реализация этой задачи приведена ниже.
Каждый из процессов инициализирует подсистему MPI с помощью вызова MPI_Init, получает свой номер и общее количество процессов посредством MPI_Comm_rank и MPI_Comm_size. Основная работа производится в функции integrate, по окончании которой процесс заканчивает работу с MPI с помощью MPI_Finalize и завершается. Функция integrate вычисляет свою часть интеграла и прибавляет его к ответу total в процессе с номером 0 посредством MPI_Reduce. Процесс с номером 0 перед окончанием своей работы выводит переменную total на экран.
Входные данные (границы отрезка и количество точек разбиения интервала) принимаются от пользователя в процессе с номером 0. Затем эти данные рассылаются всем остальным процессам с помощью MPI_Bcast.
/* Вычисление определенного интеграла */
#include «mpi.h»
/* Интегрируемая функция */
double f(double x)
/* Вычислить интеграл по отрезку [a,b] с числом
точек разбиений n по формуле трапеций */
double integrate(double a, double b, int n)
double res; /* результат */
double h; /* шаг интегрирования */
return res;
int main(int argc, char *argv[])
int my_rank; /* ранг текущего процесса */
int numprocs; /* общее число процессов */
double a; /* левый конец интервала */
double b; /* правый конец интервала */
int n; /* число точек разбиения */
double len; /* длина отрезка интегрирования
для текущего процесса*/
double local_a; /* левый конец интервала для
текущего процесса */
double local_b; /* правый конец интервала для
текущего процесса */
int local_n; /* число точек разбиения для
текущего процесса */
double local_res;/* значение интеграла в текущем
double result; /* результат интегрирования */
double wtime; /* время работы программы */
/* Начать работу с MPI */
/* Получить номер текущего процесса
в группе всех процессов */
/* Получить общее количество запущенных
/* Получить данные */
if (my_rank == 0)
/* Рассылаем данные из процесса 0 остальным */
/* Синхронизация процессов */
/* Запускаем таймер */
/* Вычисляем отрезок интегрирования
для текущего процесса */
local_a = a + my_rank*len;
local_b = local_a + len;
/* Вычислить интеграл на каждом из процессов */
local_res = integrate(local_a, local_b,
/* Сложить все ответы и передать процессу 0 */
MPI_Reduce(result, 1, MPI_DOUBLE,
MPI_SUM, 0, MPI_COMM_WORLD);
/* Синхронизация процессов */
/* Вычисляем время работы */
wtime = MPI_Wtime() — wtime;
/* Напечатать ответ */
if (my_rank == 0)
printf(«Integral from %.2lf to %.2lf=%.8lfn»,
printf(«Working time: %.2lf secondsn»,
/* Заканчиваем работу с MPI */
Имеет смысл поэкспериментировать с количеством работающих параллельно процессов, чтобы выяснить, как меняется время работы программы.
Источник: studfile.net
Презентация, доклад Параллельное программирование на основе MPI. (Раздел 4.1)
Вы можете изучить и скачать доклад-презентацию на тему Параллельное программирование на основе MPI. (Раздел 4.1). Презентация на заданную тему содержит 56 слайдов. Для просмотра воспользуйтесь проигрывателем, если материал оказался полезным для Вас — поделитесь им с друзьями с помощью социальных кнопок и добавьте наш сайт презентаций в закладки!
Презентации » Информатика » Параллельное программирование на основе MPI. (Раздел 4.1)
























>»>