Параллельное программирование на основе MPI
5.2. Введение в разработку параллельных программ с использованием MPI
5.2.1. Основы MPI
Приведем минимально необходимый набор функций MPI, достаточный для разработки сравнительно простых параллельных программ.
5.2.1.1. Инициализация и завершение MPI-программ
Первой вызываемой функцией MPI должна быть функция:
int MPI_Init(int *argc, char ***argv),
где
- argc — указатель на количество параметров командной строки,
- argv — параметры командной строки,
применяемая для инициализации среды выполнения MPI -программы. Параметрами функции являются количество аргументов в командной строке и адрес указателя на массив символов текста самой командной строки.
Последней вызываемой функцией MPI обязательно должна являться функция:
int MPI_Finalize(void).
Как результат, можно отметить, что структура параллельной программы, разработанная с использованием MPI, должна иметь следующий вид:
#include "mpi.h" int main(int argc, char *argv[]) { <программный код без использования функций MPI> MPI_Init(&argc, &argv); <программный код с использованием функций MPI> MPI_Finalize(); <программный код без использования функций MPI> return 0; }
Следует отметить:
- файл mpi.h содержит определения именованных констант, прототипов функций и типов данных библиотеки MPI ;
- функции MPI_Init и MPI_Finalize являются обязательными и должны быть выполнены (и только один раз) каждым процессом параллельной программы ;
- перед вызовом MPI_Init может быть использована функция MPI_Initialized для определения того, был ли ранее выполнен вызов MPI_Init, а после вызова MPI_Finalize – MPI_Finalized2Эта функция появилась только в стандарте MPI версии 2.0. аналогичного предназначения.
Рассмотренные примеры функций дают представление синтаксиса именования функций в MPI. Имени функции предшествует префикс MPI, далее следует одно или несколько слов названия, первое слово в имени функции начинается с заглавного символа, слова разделяются знаком подчеркивания. Названия функций MPI, как правило, поясняют назначение выполняемых функцией действий.
5.2.1.2. Определение количества и ранга процессов
Определение количества процессов в выполняемой параллельной программе осуществляется при помощи функции:
int MPI_Comm_size(MPI_Comm comm, int *size),
где
- comm — коммуникатор, размер которого определяется,
- 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(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &ProcNum); MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); <программный код с использованием функций MPI> MPI_Finalize(); <программный код без использования функций MPI> return 0; }
Следует отметить:
- коммуникатор MPI_COMM_WORLD, как отмечалось ранее, создается по умолчанию и представляет все процессы выполняемой параллельной программы ;
- ранг, получаемый при помощи функции MPI_Comm_rank, является рангом процесса, выполнившего вызов этой функции, т. е. переменная ProcRank примет различные значения у разных процессов.
5.2.1.3. Передача сообщений
Для передачи сообщения процесс - отправитель должен выполнить функцию:
int MPI_Send(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm),
где
- buf — адрес буфера памяти, в котором располагаются данные отправляемого сообщения;
- count — количество элементов данных в сообщении;
- type — тип элементов данных пересылаемого сообщения;
- dest — ранг процесса, которому отправляется сообщение;
- tag — значение-тег, используемое для идентификации сообщения;
- comm — коммуникатор, в рамках которого выполняется передача данных.
Для указания типа пересылаемых данных в MPI имеется ряд базовых типов, полный список которых приведен в табл. 5.1.
Следует отметить:
- отправляемое сообщение определяется через указание блока памяти (буфера), в котором это сообщение располагается. Используемая для указания буфера триада (buf, count, type) входит в состав параметров практически всех функций передачи данных;
- процессы, между которыми выполняется передача данных, в обязательном порядке должны принадлежать коммуникатору, указываемому в функции MPI_Send ;
- параметр tag используется только при необходимости различения передаваемых сообщений, в противном случае в качестве значения параметра может быть использовано произвольное положительное целое число3Максимально возможное целое число, однако, не может быть больше, чем определяемая реализацией константа MPI_TAG_UB . (см. также описание функции MPI_Recv ).
Сразу же после завершения функции MPI_Send процесс -отправитель может начать повторно использовать буфер памяти, в котором располагалось отправляемое сообщение. Также следует понимать, что в момент завершения функции MPI_Send состояние самого пересылаемого сообщения может быть совершенно различным: сообщение может располагаться в процессе -отправителе, может находиться в состоянии передачи, может храниться в процессе -получателе или же может быть принято процессом -получателем при помощи функции MPI_Recv. Тем самым, завершение функции MPI_Send означает лишь, что операция передачи начала выполняться и пересылка сообщения рано или поздно будет выполнена.
Пример использования функции будет представлен после описания функции MPI_Recv.