Распределенное программирование с использованием MPI Часть 2 | |
Автор: Вадим Хохлов |
Инициализация библиотеки Параллельное1 приложение состоит из нескольких процессов или задач, которые выполняются одновременно. Библиотека поддержки MPI самостоятельно создает указанное число процессов. Когда программа получает управление, все процессы уже созданы. Можно указать, что на одном процессоре следует запустить несколько задач. В этом случае их праллельное выполнение будет эмулироваться. Для проведения расчетов процессы объединяются в группы. Каждый процесс в группе имеет уникальный номер. Обычно процесс с номером 0 считается главным и управляет остальными. Особенностью MPI является понятие области связи (communication domains). При запуске приложения все процессы помещаются в одну область связи. В дальнейшем можно создавать новые области на основе существующих. Все области связи имеют независимую систему нумерации процессов. Программисту доступен коммуникатор - описатель области связи. Большинство функций библиотеки в качесте одного из параметров принимают коммуникатор, который ограничивает сферу их действия указанной областью. Начальная область связи, которая создается автоматически при старте, использует коммуникатор MPI_COMM_WORLD. Прототипы большинства функций библиотеки объявлены в файле "mpi.h". Существует несколько функций, которые должны вызываться любой распределенной программой (см. табл.1). Табл. 1. Функции инициализации и деинициализации библиотеки MPI
Библиотека также содержит набор информационных функций. В табл. 2 представлены две из них. Табл. 2. Информационные функции MPI
Теперь мы можем написать простейшую распределенную программу: /*файл first.c */ #include /* прототипы функций MPI */ #include "mpi.h" int main(int argc, char **argv) { int myrank, size; /* Инициализируем библиотеку */ MPI_Init (&argc, &argv); /* определяем число процессов */ MPI_Comm_size (MPI_COMM_WORLD, &size); /* Определяем номер процесса */ MPI_Comm_rank (MPI_COMM_WORLD, &myrank); /* выводим полученную информацию */ printf("Number %d size %d\n", myrank, size); /* Закрываем библиотеку */ MPI_Finalize(); return 0; } Компиляция и запуск параллельных программ Для компиляции C-программ, использующих MPICH, следует использовать скрипт mpicc. Он запускается с теми же параметрами, что и компилятор C и добавляет параметры, необходимые для подключения библиотеки: mpicc -o first first.c Для запуска программы используется скрипт mpirun. Ему необходимо указать число создаваемых процессов и название исполняемого файла: mpirun -np 4 ./first После запуска Вы увидите на экране следующее: Number 3 size 4 Number 0 size 4 Number 2 size 4 Number 1 size 4 Порядок выполения процессов неопределен. Поэтому и строки на экране будут появлятся в произвольном порядке. Чтобы убедиться в этом, несколько раз запустите программу. Передача и прием сообщений Как уже отмечалось, одним из способов взаимодействия распределенных процессов является передача сообщений. MPI (Message Passing Interface) определяет множество функций, с помощью которых задачи могут принимать и передавать сообщения. Каждое сообщение имеет набор атрибутов - номер процесса-отправителя, номер процесса-получателя и идентификатор сообщения. Идентификатором сообщения является целое число, лежащее в диапазоне от 0 до 32767. Две самые простые функции для приема/передачи представлены в табл. 3. Табл. 3. Простейшие функции MPI приема/передачи
Данные функции выполняют блокирующую приемо-передачу, т.е. при их использовнии управление вызывающему процессу возвращается только после того, как данные приняты или переданы (или скопированы во временный буфер). Типы данных в MPI Функциям MPI необходимо указывать тип передаваемых данных. Это связано с тем, что процессы распределенной программы могут одновременно выполняться на машинах, имеющих разную архитектуру, порядок байт и представление данных. Поэтому все функции приема и передачи в MPI оперируют не количеством передаваемых байт, а количеством ячеек, тип которых задается параметром функции. В MPI используется специальный тип - MPI_Datatype - тип "описатель типов", каждая его переменная описывает для MPI один тип. Для каждого базового типа данных, имеющегося в используемом языке программирования, в библиотеке MPI определены соответсвующие константы 2. Например, в табл. 4 представлены константы, определенные для языка C. Табл. 4. Константы MPI и типы данных C
Простейшая программа, выполняющая передачу данных между процессами, может имееть следующий вид: /* Файл mpi2.c */ #include #include "mpi.h" int main(int argc, char **argv) { int me; char message[20]; MPI_Status status; MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &me); if (me ==0 ) /* это главный процесс */ { strcpy (message, "Hello, there"); /* пошлем процессу 1 строку символов; сообщение имеет номер 99*/ MPI_Send(message, strlen(message) + 1, MPI_CHAR, 1, 99, MPI_COMM_WORLD); } else /* это вспомогательный процесс*/ { /* Ждем от процесса 0 сообщения 99, содержащего строку символов */ MPI_Recv(message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status); printf("proc %d receiveds :%s\n", me, message); } MPI_Finalize(); return 0; } Программа рассчитана только на два процесса, поэтому ее надо запускать следующим образом: mpirun -np 2 ./mpi2 На экран она выведет следующее: proc 1 receiveds :Hello, there Если скрипту mpirun указать ключ -l, то перед каждой выводимой строкой будет печататься номер процесса, который напечатал эту строку: mpirun -l -np 2 ./mpi2 1: proc 1 receiveds :Hello, there Заключение Мы рассмотрели основы программирования распределенных программ с использованием библиотеки MPI. В следующей статье будут рассмотрены более сложные и более мощные функции и примеры. 1Параллельные программы, выполняющиеся на разных машинах, называют распределенными, чтобы подчеркнуть их отличие от программ, выполняющися на одной многопроцессорной системе (как правило, с разделяемой памятью). В статье термины параллельный и распределенный используются как синонимы. 2В MPI существуют механизмы создания пользовательских типов данных. Они будут рассмотрены в следующих статьях. |