Вы находитесь на странице: 1из 5

ТЕХНОЛОГИЯ ПАРАЛЛЕЛЬНОГО ПРОГРАММИРОВАНИЯ MPI

Лекция 1. Технология MPI. SPMD-модель программирования. Общие функции MPI.

MPI (Message Passing Interface) – наиболее распространенная технология


программирования для систем с распределенной памятью. Передача сообщений является
основным способом взаимодействия для систем такого вида.
MPI поддерживает работу с языками Fortran и Си.
Интерфейс MPI поддерживает создание параллельных программ в стиле MIMD (Multiple
Instruction Multiple Data), что подразумевает объединение процессов, выполняющих разные
программы. Написание и отладка приложений в стиле MIMD представляет сложную задачу,
поэтому на практике основой моделью программирования является SPMD (Single Program
Multiple Data) стиль программирования. SPMD модель подразумевает, что все процессы
исполняют один и тот же код.
MPI поддерживает работу с потоками.
MPI-программа с точки зрения выполнения – это множество параллельных
взаимодействующих процессов. Все процессы порождаются один раз, образуя параллельную
часть программы. В ходе выполнения программы порождение новых процессов и их
уничтожение не допускается. Каждый процесс работает в своем адресном пространстве –
никаких общих переменных или данных по технологии MPI нет. Основным способом
взаимодействия и синхронизации процессов является обмен сообщениями.
Все элементы MPI синтаксически начинаются с префикса MPI_, последующее за ним
наименование начинается с заглавной буквы (для Си). Все описания интерфейса собраны в
файле mpi.h (для Си) и mpif.h (для фортрана).
Для локализации взаимодействия параллельных процессов программы можно
объединять процессы в группы, предоставляя им отдельную среду для обмена данными -
коммуникатор. Состав групп абсолютно произволен. Группы могут пересекаться, входить друг в
друга. Процессы могут взаимодействовать только внутри группы. Сообщения посылаемые
процессом в разных коммуникаторах не пересекаются и не мешают друг другу.
В Си для коммуникатора используется предопределенный тип MPI_Comm.
Изначально при старте программы все процессы имеют один общий коммуникатор –
MPI_COMM_WORLD. Этот коммуникатор существует всегда и служит для взаимодействия
всех запущенных MPI-процессов. Кроме этого коммуникатора на старте определен
коммуникатор MPI_COMM_SELF, содержащий только один текущий процесс, и коммуникатор
MPI_COMM_NULL, не содержащий ни одного процесса. Все взаимодействия между
процессами протекают только в рамках одного из коммуникаторов.
Каждый MPI-процесс в каждой группе (коммуникаторе), в которую он входит, имеет свой
уникальный номер, который является целым неотрицательным числом. С помощью этого
атрибута осуществляется идентификация процесса и соответственно возможность программной
реализации отдельной ветви исполнения. В одном и том же коммуникаторе процессы имеют
разные номера. Один и тот же процесс в разных коммуникаторах может иметь разные номера.
Если группа объединяет N процессов, то их нумерация лежит в отрезке [0;N-1].
Таким образом каждый процесс имеет два атрибута (набор пар атрибутов): коммуникатор и
номер процесса в коммуникаторе.

1
Основным способом взаимодействия и синхронизации процессов является посылка
сообщения. Сообщение – это набор данных некоторого типа. В MPI для передачи данных
определены свои типы - аналоги типов для каждого языка привязки и реализованы их
соотношение друг к другу.
Примеры:
MPI_INT – int,
MPI_LONG – long int,
MPI_DOUBLE – double,
MPI_CHAR – char,
MPI_UNSIGNED_CHAR – unsigned char.
Кроме этого определены типы MPI_BYTE и MPI_PACKED.
Каждое сообщение имеет несколько атрибутов, в частности номер процесса отправителя, номер
процесса получателя, тег сообщения и коммуникатор. Эти атрибуты позволяют однозначно
идентифицировать сообщение в рамках коммуникатора.
Большинство функций MPI возвращают информацию об успешности выполнения.
Признак успеха – предопределенное в mpi.h значение MPI_SUCCESS.

Общие функции MPI


1) Параллельная часть программы начинается с вызова функции MPI_Init()
int MPI_Init (int *argc, char ***argv );
Функция MPI_Init устанавливает окружение, в котором MPI-программа будет
выполняться., при этом она использует аргументы из командной строки запуска
программы. Наличие этой функции обязательно. Все остальные вызовы MPI-функций
производятся только после вызова MPI_Init.
2) Завершение параллельной части программы заканчивается вызовом MPI_Finalize()
int MPI_Finalize (void);
Функция уничтожает окружение MPI, созданное функцией MPI_Init. Использование
функций MPI после вызова MPI_Finalize запрещены. Естественно, что во избежание
блокировки программы все коммуникации связанные с процессом должны быть
завершены.

#include <mpi.h>
int main (int argc, char **argv)
{
…….
MPI_Init (&argc, &argv);
/*Параллельная часть программы*/
MPI_Finalize ();
……..
}
3) Проверка инициализации MPI
int MPI_Initialized (int *flag);
Функция позволяет проверить инициализирован ли MPI вызовом функции MPI_Init. Это
единственная функция, которая может быть вызвана до MPI_Init.
4) Прерывание программы с помощью MPI_Abort

2
int MPI_Abort (MPI_Comm comm, int error);
Функция совершает по возможности корректное прерывание и завершение параллельной
части программы. Стандарт MPI предполагает реализацию этой функции для
коммуникатора MPI_COMM_WORLD. Поведение функции в случае пользовательских
коммуникаторов определяется реализацией MPI.
5) Таймер
double MPI_Wtime (void);
Функция возвращает на вызвавшем процессе время в секундах, прошедшее с некоторого
момента в прошлом. Возвращаемое время локально по отношению к процессору на
котором выполняется процесс. Соотношение к временам, получаемым в процессах,
отображенных на другие процессоры, определяется тем, синхронизированы ли они или
нет.
6) Точность таймера
double MPI_Wtick (void);
Возвращает точность функции-таймера MPI_Wtime.
7) Определение процессора, но который отобразился процесс
int MPI_Get_processor_name (char *name, int *resultlen);
Функция возвращает имя процессора, на котором выполняется процесс. Область памяти
name должна содержать достаточно места для вывода имени
(MPI_MAX_PROCESSOR_NAME). Таким образом функция позволяет определить как
произошло отображение параллельных процессов программы на физическую сеть
процессоров.
8) Определение размера коммуникатора
int MPI_Comm_size (MPI_Comm comm, int *size);
Функция возвращает размер (кол-во процессов) size коммуникатора comm.
9) Идентификация процесса
int MPI_Com_rank (MPI_Comm comm, int *rank);
Функция возвращает номер процесса (его ранг) rank в коммуникаторе comm.

Примеры
#include <mpi.h>
int main (int argc, char **argv)
{
int myrank = 0;
int p = 0;

MPI_Init (&argc, &argv);


MPI_Comm_size (MPI_COMM_WORLD, &p);
MPI_Comm_rank (MPI_COMM_WORLD, &myrank);
printf (“Hello from process %d (communicator size: %d)\n”,
myrank, p);
MPI_Finalize ();
return 0;
}

3
#include <mpi.h>
int main (int argc, char **argv)
{
int source = 0;
int dest = 0;
int tag = 55;
char message[100];
int myrank = 0;
int p = 0;
MPI_Status status;

MPI_Init (&argc, &argv);


MPI_Comm_size (MPI_COMM_WORLD, &p);
MPI_Comm_rank (MPI_COMM_WORLD, &myrank);
if (myrank)
{
dest = 0;
sprintf (message, “Hello from process %d”, myrank);
MPI_Send (message, strlen(message)+1, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
}
else
{
for (source = 1; source < p; source++)
{
MPI_Recv (message, 100, MPI_CHAR, source, tag, MPI_COMM_WORLD, &status);
printf (“%s\n”, message);
}
}

MPI_Finalize ();
return 0;
}

#include <mpi.h>
int main (int argc, char **argv)
{
int n = 1000;
double *array = NULL;
double *block = NULL;
double *pa;
double subsum = 0.0;
double sum = 0.0;
int myrank = 0;
int p = 0;
4
MPI_Init (&argc, &argv);
MPI_Comm_size (MPI_COMM_WORLD, &p);
MPI_Comm_rank (MPI_COMM_WORLD, &myrank);

if (!myrank)
{
array = (double*) malloc (n*sizeof(double));
f = fopen(“data”, “r”);
for (pa=array; pa != array+n; pa++ ) fscanf (f, “%e”, pa);
}

block = (double*) malloc (n*sizeof(double)/p);


MPI_Scatter (array, n/p, MPI_DOUBLE, block, n/p, MPI_DOUBLE,
0, MPI_COMM_WORLD);
for (pa=block, subsum=0.0; pa != block+n/p; pa++) subsum += *pa;
MPI_Reduce (subsum, sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (!myrank) printf (“Sum=%e\n”, sum);

if (array!=NULL) free(array);
free (block);
MPI_Finalize ();
return 0;
}