Пособие по MPI (реферат)
Описание файла
Документ из архива "Пособие по MPI (реферат)", который расположен в категории "". Всё это находится в предмете "параллельные системы и параллельные вычисления" из 9 семестр (1 семестр магистратуры), которые можно найти в файловом архиве НИУ «МЭИ» . Не смотря на прямую связь этого архива с НИУ «МЭИ» , его также можно найти и в других разделах. Архив можно найти в разделе "остальное", в предмете "параллельные системы и параллельные вычисления" в общих файлах.
Онлайн просмотр документа "Пособие по MPI (реферат)"
Текст из документа "Пособие по MPI (реферат)"
Московский Энергетический Институт
(Технический Университет)
Кафедра Прикладной Математики
Реферат
на тему «Пособие по MPI »
Выполнил: Ясенков Е.М.
Группа: А-13-03
Москва 2007
1. MPI_Init, MPI_Finalize, “Hello, World” с анализом результата MPI_Init
Функция MPI_Init() применяется для инициализации среды выполнения MPI-программы. Параметрами функции являются количество аргументов в командной строке и адрес указателя на массив символов текста самой командной строки.
Функция MPI_Finalize() применяется для завершения выполнения MPI-программы.
Как результат, можно отметить, что структура параллельной программы, разработанная с использованием MPI, должна иметь следующий вид:
#include "mpi.h"
int main(int argc, char *argv[])
{
// программный код без использования функций MPI
MPI_Init(&argc, &argv);
// программный код с использованием функций MPI
MPI_Finalize();
// программный код без использования функций MPI
return 0;
}
Все функции MPI (кроме MPI_Wtime и MPI_Wtick) возвращают в качестве своего значения код завершения. При успешном выполнении функции возвращаемый код равен MPI_SUCCESS. Другие значения кода завершения свидетельствуют об обнаружении тех или иных ошибочных ситуаций в ходе выполнения функций. Для выяснения типа обнаруженной ошибки используются предопределенные именованные константы, среди которых:
MPI_ERR_BUFFER — неправильный указатель на буфер;
MPI_ERR_TRUNCATE — сообщение превышает размер приемного буфера;
MPI_ERR_COMM — неправильный коммуникатор;
MPI_ERR_RANK — неправильный ранг процесса и др.
Далее следует пример программы “Hello, World” с анализом результата MPI_Init():
#include "mpi.h"
int main(int argc, char* argv[])
{
int ProcRank, Error;
Error = MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank);
printf("Hello, World! from process %2d... ", ProcRank);
if (Error == MPI_SUCCESS)
printf("Ошибок нет\n");
else if (Error == MPI_ERR_RANK)
printf("Ошибка - неправильный ранг процесса");
else if (Error == MPI_ERR_UNKNOWN)
printf("Неизвестная ошибка");
MPI_Finalize();
return 0;
}
Результат выполнения программы на 5 процессах:
Hello, World! from process 2... Ошибок нет
Hello, World! from process 3... Ошибок нет
Hello, World! from process 4... Ошибок нет
Hello, World! from process 0... Ошибок нет
Hello, World! from process 1... Ошибок нет
---------------------------------------------------------------------------
2. MPI_Send, MPI_Recv, анализ структуры MPI_Status
Функция MPI_Send применяется для передачи сообщения:
int MPI_Send(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm), где
-
buf — адрес буфера памяти, в котором располагаются данные отправляемого сообщения;
-
count — количество элементов данных в сообщении;
-
type — тип элементов данных пересылаемого сообщения;
-
dest — ранг процесса, которому отправляется сообщение;
-
tag — значение-тег, используемое для идентификации сообщения;
-
comm — коммуникатор, в рамках которого выполняется передача данных.
Функция MPI_Recv применяется для получения сообщения:
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 – указатель на структуру данных с информацией о результате выполнения операции приема данных.
Структура MPI_Status (взято из файла mpi.h):
typedef struct MPI_Status {
int count; // необязательное поле - количество элементов данных в сообщении
int cancelled; // необязательное поле
int MPI_SOURCE; // ранг процесса–отправителя принятого сообщения;
int MPI_TAG; // тэг принятого сообщения
int MPI_ERROR; // код ошибки
} MPI_Status;
В следующем примере процесс 0 посылает сообщение из 12 символов процессу 1 и тот также в ответ шлет сообщение процессу 0:
# include "mpi.h"
# include
int main(int argc, char** argv)
{
int numtasks, rank, rc, dest, source, tag = 1;
char* outmsg1 = "Hello first\0";
char* outmsg2 = "Hello zero\0";
char inmsg[12];
MPI_Status stat;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if(rank == 0){
dest = 1;
source = 1;
rc = MPI_Send(outmsg1, 12, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
rc = MPI_Recv(inmsg, 12, MPI_CHAR, source, tag, MPI_COMM_WORLD, &stat);
printf("Process %d received message: \"%s\"\n", rank, inmsg);
}
else if(rank == 1){
dest = 0;
source = 0;
rc = MPI_Send(outmsg2, 12, MPI_CHAR, dest, tag, MPI_COMM_WORLD);
rc = MPI_Recv(inmsg, 12, MPI_CHAR, source, tag, MPI_COMM_WORLD, &stat);
printf("Process %d received message: \"%s\"\n", rank, inmsg);
}
MPI_Finalize();
return 0;
}
Результат выполнения программы на 2 процессах:
Process 1 received message: "Hello first"
Process 0 received message: "Hello zero"
-----------------------------------------------------------------------------------------------------------------
3. Разрешение взаимной блокировки Send/Recv с помощью функции MPI_Sendrecv
Пример взаимной блокировки этих двух функций:
Допустим, у нас есть несколько процессов, которые взаимодействуют следующим образом:
Каждый процесс содержит следующий код:
for(i=0; i { MPI_Send(…); } for(i=0; i { MPI_Recv(…); } Если запустить эти процессы, мы получим тупик, так как процессы зайдут в функцию MPI_Send() и будут ждать ее завершения. А для ее завершения необходимо выполнение функции MPI_Recv, которая никогда не выполнится. Чтобы избежать взаимной блокировки можно выполнить взаимный обмен данными между процессами, используя совмещенную операцию MPI_Sendrecv. int MPI_Sendrecv(void *sbuf, int scount, MPI_Datatype stype, int dest, int stag, void *rbuf, int count, MPI_Datatype rtype, int source, int rtag, MPI_Comm comm, MPI_Status *status), где sbuf, scount, stype, dest, stag — параметры передаваемого сообщения; rbuf, rcount, rtype, source, rtag — параметры принимаемого сообщения; comm — коммуникатор, в рамках которого выполняется передача данных; status — структура данных с информацией о результате выполнения операции. Тогда каждый процесс будет содержать следующий код: for(i=0; i { MPI_Sendrecv(…); } ------------------------------------------------------------------------ 4. Пример использования MPI_Waitall, MPI_Waitany Процедура MPI_Waitall: int MPI_Waitall(int count, MPI_Request *requests, MPI_Status *statuses), где – count - число идентификаторов; – requests - массив идентификаторов асинхронного приема или передачи; – OUT statuses - параметры сообщений. Выполнение процесса блокируется до тех пор, пока все операции обмена, ассоциированные с указанными идентификаторами, не будут завершены. Если во время одной или нескольких операций обмена возникли ошибки, то поле ошибки в элементах массива statuses будет установлено в соответствующее значение. Пример. Сеть из 4 узлов с круговой топологией. Каждый узел посылает двум соседям сообщения и ждет ответа от них. Работа каждого узла не завершится до тех пор, пока все 4 операции не будут выполнены. # include # include int main(int argc, char** argv) { int numtasks, rank, next, prev, buf[2], tag1 = 1, tag2 = 2; MPI_Request reqs[4]; MPI_Status stats[4]; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); MPI_Comm_rank(MPI_COMM_WORLD, &rank); prev = rank - 1; next = rank + 1; if (rank == 0) prev = numtasks - 1; if (rank == (numtasks - 1)) next = 0; MPI_Irecv(&buf[0], 1, MPI_INT, prev, tag1, MPI_COMM_WORLD, &reqs[0]); MPI_Irecv(&buf[1], 1, MPI_INT, next, tag2, MPI_COMM_WORLD, &reqs[1]); MPI_Isend(&rank, 1, MPI_INT, prev, tag2, MPI_COMM_WORLD, &reqs[2]); MPI_Isend(&rank, 1, MPI_INT, next, tag1, MPI_COMM_WORLD, &reqs[3]); MPI_Waitall(4, reqs, stats); printf("Node %d: all ok!\n", rank); MPI_Finalize(); } Результат выполнения программы на 4 процессах: Node 1: all ok! Node 3: all ok! Node 2: all ok! Node 0: all ok! Процедура MPI_Waitany: int MPI_Waitany(int count, MPI_Request *requests, int *index, MPI_Status *status), где – count - число идентификаторов; – requests - массив идентификаторов асинхронного приема или передачи; – OUT index - номер завершенной операции обмена; – OUT status - параметры сообщений. Выполнение процесса блокируется до тех пор, пока какая-либо операция обмена, ассоциированная с указанными идентификаторами, не будет завершена. Если несколько операций могут быть завершены, то случайным образом выбирается одна из них. Параметр index содержит номер элемента в массиве requests, содержащего идентификатор завершенной операции. Пример. Изменим предыдущий пример используя функцию MPI_Waitany вместо MPI_Waitall. # include # include int main(int argc, char** argv) { int numtasks, rank, next, prev, buf[2], tag1 = 1, tag2 = 2; int *i; MPI_Request reqs[4]; MPI_Status stats[4]; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); MPI_Comm_rank(MPI_COMM_WORLD, &rank); prev = rank - 1; next = rank + 1; if (rank == 0) prev = numtasks - 1; if (rank == (numtasks - 1)) next = 0; MPI_Irecv(&buf[0], 1, MPI_INT, prev, tag1, MPI_COMM_WORLD, &reqs[0]); MPI_Irecv(&buf[1], 1, MPI_INT, next, tag2, MPI_COMM_WORLD, &reqs[1]); MPI_Isend(&rank, 1, MPI_INT, prev, tag2, MPI_COMM_WORLD, &reqs[2]); MPI_Isend(&rank, 1, MPI_INT, next, tag1, MPI_COMM_WORLD, &reqs[3]); MPI_Waitany(4, reqs, &i, stats); printf("Node %d: all ok!\n", rank); MPI_Finalize(); } Результат выполнения программы на 4 процессах: Node 1: all ok! Node 3: all ok! Node 2: all ok! Node 0: all ok! -------------------------------------------------- 5. Пример использования MPI_Bcast Broadcast - передача сообщения от одного процесса ко всем остальным процессам группы, включая его самого. Схематически broadcast можно изобразить следующим образом: int MPI_Bcast(void *buff, int count, MPI_Datatype datatype, int root, MPI_Comm comm), где buff - адрес начала буфера, хранящего передаваемое сообщение count - количество передаваемых элементов datatype - тип передаваемых элементов root - номер корневого процесса, т.е. номер процесса от которого будет передаваться сообщение всем остальным процессам comm - идентификатор группы Пример. Каждый не root процесс в буфере sbuf хранит строку "I am not root", а root-процесс кладет себе в буфер sbuf строку "Hello from root" и рассылает ее при помощи MPI_Bcast всем остальным. В результате, у каждого не root процесса в буфере оказывается именно это сообщение. # include # include # include int main(int argc, char** argv) { int numtasks, rank, root; char sbuf[20]; strcpy(sbuf, "I am not root\0"); root = 1; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if(rank == root) strcpy(sbuf, "Hello from root\0"); MPI_Bcast(sbuf, 16, MPI_CHAR, root, MPI_COMM_WORLD); if(rank == root) strcpy(sbuf, "I am root\0"); printf("I am %d. Message received: %s\n", rank, sbuf); MPI_Finalize(); return 0; } Результат выполнения программы на 5 процессах: I am 0. Message received: Hello from root I am 3. Message received: Hello from root I am 4. Message received: Hello from root I am 1. Message received: I am root I am 2. Message received: Hello from root -------------------------------------------------------------------------- 6. Пример использования MPI_Gatherv и MPI_Scatterv Функция MPI_Gatherv: int MPI_Gatherv (void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int *recvcnts, int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm), где sendbuf - Адрес, по которому находятся отправляемые данные sendcount - Число отправляемых данных (в каждом процессе — свое) sendtype - Тип отправляемых данных recvcounts - Целочисленный массив размера, равного числу процессов в коммуникаторе; каждый элемент этого массива содержит число элементов, принимаемых от процесса с рангом, равным номеру этого элемента. displs - Целочисленный массив размера, равного числу процессов в коммуникаторе. i-тый элемент определяет смещение (в элементах) относительно адреса recvbuf, по которому разместить данные, приходящие от процесса с рангом i. (имеет значение только в процессе с рангом root) recvtype - Тип принимаемых данных (имеет значение только в процессе с рангом root) root - ранг процесса, который принимает данные. comm - communicator (handle) OUT recvbuf - Адрес, по которому принимаются данные. (имеет значение только в процессе с рангом root) Пример. Здесь каждый процесс посылает 100 чисел типа int корневому процессу, но каждое множество (100 элементов) размещается с некоторым шагом (stride) относительно конца размещения предыдущего множества. Чтобы получить этот эффект нужно использовать MPI_GATHERV и аргумент displs. Полагаем, что stride > 100. MPI_Comm comm; int gsize,sendarray[100]; int root, *rbuf, stride; int *displs,i,*rcounts; ... MPI_Comm_size(comm, &gsize); rbuf = (int *)malloc(gsize*stride*sizeof(int)); displs = (int *)malloc(gsize*sizeof(int)); rcounts = (int *)malloc(gsize*sizeof(int)); for (i=0; i displs[i] = i*stride; rcounts[i] = 100; } MPI_Gatherv(sendarray, 100, MPI_INT, rbuf, rcounts, displs, MPI_INT, root, comm); Функция MPI_Scatterv: MPI_SCATTERV(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcount, ecvtype, IN sendbuf адрес буфера посылки (альтернатива, используется только корневым процессом) IN sendcounts целочисленный массив (размера группы), определяющий число элементов, для отправки каждому процессу IN displs целочисленный массив (размера группы). Элемент i указывает смещение (относительно sendbuf, из которого берутся данные для процесса take the i) IN sendtype тип элементов посылающего буфера (дескриптор) OUT recvbuf адрес принимающего буфера (альтернатива) IN recvcount число элементов в посылающем буфере (целое) IN recvtype тип данных элементов принимающего буфера (дескриптор) IN root номер посылающего процесса (целое) IN comm коммуникатор (дескриптор) Пример. Корневой процесс рассылает множества из 100 чисел типа int остальным процессам, но множества размещены в посылающем буфере с шагом stride, поэтому нужно использовать MPI_Scatterv. Полагаем stride > 100. MPI_Comm comm; int gsize,*sendbuf; int root, rbuf[100], i, *displs, *scounts; ... MPI_Comm_size(comm, &gsize); sendbuf = (int*)malloc(gsize*stride*sizeof(int)); ... displs = (int*)malloc(gsize*sizeof(int)); scounts = (int*)malloc(gsize*sizeof(int)); for (i=0; i displs[i] = i*stride; scounts[i] = 100; } MPI_Scatterv(sendbuf, scounts, displs, MPI_INT, rbuf, 100, MPI_INT, root, comm); 9
root, comm)