Кафедра СКС |
Лабораторная работа №2 |
ДонГТУ |
СКС-08-2 |
Знакомство с MPI. Функции для двухточечного обмена сообщениями (блокирующие). |
Белик Александр |
Цель лабораторной работы
Получить основы многопоточного программирования с использованием библиотеки MPI.
Научится использовать механизмы передачи сообщений типа точка-точка с использованием различных функций.
Порядок выполнения работы
1. В приведенных заданиях дописать и исправить приведенные фрагменты кода.
2. Произвести компиляцию и запуск программ (./zadanie).
3. получить индивидуальное задание для защиты.
Содержание отчета
1. Группа ФИО.
2. Тема лабораторной работы.
3. Цель лабораторной работы.
4. Содержание файлов с кодом програм.
Теория.
Трансляция MPI-программ
Утилиты трансляции и сборки находятся в каталоге /user/locla/mpich1/bin. Его следует включить в путь поиска исполняемых файлов.
Для трансляции и компоновки программ на языке C++ используется команда mpiCC, для трансляции программ на языке C— команда mpicc.
Информацию о ключах можно найти на справочных страницах MPICH (для их просмотра применяют команду man). Пример применения команды:
mpicc -o test1 test1.c
Для компиляции программ желательно использовать makefile
Пример содержания makefile:
CC=/usr/local/mpich1/bin/mpicc NAME=svv all:$(NAME) clean $(NAME): $(NAME).o $(CC) $(NAME).c -lm -o $@ $(NAME)o: $(NAME).c $(CC) -c $(NAME).c clean: rm ./*.o
Пояснения:
строка для определения макроса компиляции программыCC=/usr/local/mpich1/bin/mpicc
строка где определяется имя программ и исходного файла NAME=svv
Выполнение mpi-программ
Для выполнения MPI-программ в MPICH используется загрузчик приложений mpirun. Он запускает указанное количество копий программы. Команда запуска:
mpiexec -np n [ключи MPI] программа [ключи и аргументы программы]
где п — число запускаемых процессов. Некоторые ключи MPI (они указываются перед именем исполняемого файла программы) приведены в таблице:
Ключ |
Описание |
-h |
Краткая информация о команде |
-machinefile name_файл |
Для запуска программы использовать компьютеров из указанного файла |
Для запуска программ можно использовать специальный скрипт например такого содержимого:
#!/bin/sh mpiexec -machinefile ./machines -np 3 ./svv > ../res
В нашем случае скрипт это просто текстовый файл (обычно для него используется расширение .sh) для которого указаны права не исполнение (chmod 7?? name.sh).
В этом примере три запускаемых экземпляра программы.
Простейшие функции mpi
MPI_Init(int *argc, char ** argv)
Производит инициализацию MPI. Должна быть первой из вызываемых MPI функций. В нее передаются адреса аргументов функции main().
Командой mpirun один и тот же исполняемый файл (точнее, файлы с одним и тем же именем в рабочей директории на разных узлах) запускаются на исполнение. Поскольку SPMD распараллеливание предполагает разделение работы между одновременно работающими устройствами, эти устройства должны выполнять различные части программы. Нет никакого смысла в том, что все устройства выполнят абсолютно одни и те же действия.
Когда исполняемый файл запускается, соответствующий процесс должен получить какой-то отличительный признак, по которому этот процесс будет идентифицироваться. Таким признаком является ранг(номер) процесса. Ранг служит двум целям: процесс определяет свою часть работы, зная свой ранг, и при обмене сообщениями между двумя процессами, эти процессам необходимо знать ранги друг друга.
Кроме того, почти всегда необходимо знать общее число процессов программы. Для этого используется следующая пара функций:
MPI_Comm_rank(MPI_Comm comm, int* rank)
записывает по адресу rank ранг вызвавшего процесса в коммуникаторе comm В разных коммуникаторах один и тот же процесс может иметь различные ранги.
MPI_Comm_size(MPI_Comm comm, int* size)
записывает по адресу size общее число процессов в коммуникаторе comm.
MPI_Finalize()
Производит корректное завершение среды MPI. После вызова этой функции нельзя вызывать другие функции MPI. Эта функция обязательно должна вызываться до завершения программы.
Первая программа "Hello, mpi world!"
#include "mpi.h" #include <stdio.h> int main(int argc, char ** argv) { int i, myrank,ranksize; MPI_Status status; MPI_Init (&argc, &argv); /* initialize MPI-system */ MPI_Comm_rank (MPI_COMM_WORLD, &myrank); /* my place in MPI system */ MPI_Comm_size (MPI_COMM_WORLD, &ranksize); /* size of MPI system */ if (myrank == 0) /* I am the MASTER */ { printf("Master %d of %d work\n", myrank,ranksize); } else { printf ("Worker %d of %d work \n", myrank,ranksize); } MPI_Finalize (); } // end MAIN function // end of hello.c program
Двухточечные блокирующие обмены
Участниками двухточечного обмена являются два процесса: процесс-отправитель и процесс-получатель. Блокирующие операции двухточечного обмена приостанавливают выполнение вызывающего процесса. Он переходит в состояние ожидания завершения передачи данных. Блокировка гарантирует выполнение действий в заданном порядке, обеспечивая предсказуемость поведения программы. С другой стороны, она создает условия для возникновения тупиковых ситуаций, когда оба процесса-участника обмена блокируются одновременно.
Функция MPI_Send.
int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
Отправляет данные, находящиеся по адресу buf в количестве count типаdatatype процессу с рангом dest с тэгом сообщения tag. Посылка сообщения происходит в коммуникаторе comm. Параметр tag необходим для того, чтобы отличать сообщения от одного и того же процесса, либо сообщения от разных процессов, если принимающий процесс ожидает сообщение от любого процесса.
Функция MPI_Recv.
int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Status *status)
Принимает данные и записывает их по адресу buf в количестве count типаdatatype от процесса с рангом dest с тэгом сообщения tag. Прием сообщения происходит в коммуникаторе comm. По адресу status записывается информация о прошедшем приеме сообщения.
typedef struct MPI_Status{
int count; //Сколько байт получено
int cancelled; //Была ли передача прервана
int MPI_SOURCE; //От кого сообщение
int MPI_TAG; //Тэг сообщения
int MPI_ERROR; //Код ошибки
} MPI_Status;
В качестве параметров source и tag могут быть переданы константыMPI_ANY_SOURCE и MPI_ANY_TAG соответственно.Значение MPI_ANY_SOURCEпараметра dest означает прием сообщения от любого процесса коммуникатора, а значениеMPI_ANY_TAG означает прием сообщения с произвольным тэгом.
Функции MPI_Send и MPI_Recv являются блокирующими. Возвращение изMPI_Send не происходит до тех пор, пока данные сообщения не будут скопированы во внутренний буфер MPI. MPI_Recv возвращает управление только после того, как сообщение было принято.
Пример
1 #include <mpi.h> 2 #include <stdio.h> 3 int main (int argc, char *argv[]) { 4 int ProcNum, ProcRank, tmp, i; 5 MPI_Status status; 6 MPI_Init ( &argc, &argv ); 7 MPI_Comm_size (MPI_COMM_WORLD, &ProcNum); 8 MPI_Comm_rank (MPI_COMM_WORLD, &ProcRank); 9 if(ProcRank == 0){ 10 printf("Hello world from process %i \n", ProcRank); 11 for(i = 1; i < ProcNum; i++){ 12 MPI_Recv(&tmp,1,MPI_INT,MPI_ANY_SOURCE,0,MPI_COMM_WORLD, &status); 13 printf("Hello world from process %i \n", tmp); 14 } 15 }else{ 16 MPI_Send(&ProcRank,1,MPI_INT,0,0,MPI_COMM_WORLD); 17 } 18 MPI_Finalize(); 19 return 0; 20 }