370 likes | 572 Views
MPI. Терминология и обозначения. MPI - message passing interface Процессы объединяются в группы . С каждой группой ассоциирован свой коммуникатор . Два основных атрибута процесса : коммуникатор (группа) и номер процесса в коммуникаторе (группе) .
E N D
MPI. Терминология и обозначения MPI - message passing interface Процессы объединяются в группы. С каждой группой ассоциирован свой коммуникатор. Два основных атрибута процесса: коммуникатор (группа) и номер процесса в коммуникаторе (группе). Номер процесса - целое неотрицательное число, являющееся уникальным атрибутом каждого процесса от 0 до N-1 (N – число процессоров в группе. Все процессы содержатся в группе с предопределенным идентификатором MPI_COMM_WORLD.
Сообщение- набор данных некоторого типа. Атрибуты сообщения: номер процесса-отправителя, номер процесса-получателя, идентификатор сообщения и др. Идентификатор сообщения (msgtag) - атрибут сообщения, являющийся целым неотрицательным числом, лежащим в диапазоне от 0 до 32767.
Общие процедуры MPI int MPI_Init( int* argc, char*** argv) - инициализация параллельной части приложения. int MPI_Finalize( void ) - завершение параллельной части приложения.
int MPI_Comm_size( MPI_Comm comm, int* size) - определение общего числа параллельных процессов в группе comm. IN comm - идентификатор группы OUT size - размер группы
int MPI_Comm_rank( MPI_comm comm, int* rank) - определение номера процесса в группе comm. Значение, возвращаемое по адресу &rank. IN comm- идентификатор группы OUT rank - номер вызывающего процесса в группе comm
Пример 1. «Привет» #include <stdlib.h> #include "mpi.h" main(int argc, char **argv) { int me, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &me); MPI_Comm_size(MPI_COMM_WORLD, &size); printf("Hi, I’m process %d of %d \n", me, size); MPI_Finalize(); }
double MPI_Wtime(void) - функция возвращает астрономическое время в секундах (вещественное число), прошедшее с некоторого момента в прошлом.
int MPI_Get_processor_name(char *name, int *len) • определяет имя процессора, на котором выполняется данная команда. • Также определяет длину имени процессора. Буффер name должен быть как минимум размером в MPI_MAX_PROCESSOR_NAME символов.
int MPI_Send (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) - функция блокирующей посылки IN buf начальный адрес буфера посылки сообщения (альтернатива) IN count число элементов в буфере посылки (неотрицательное целое) IN datatype тип данных каждого элемента в буфере передачи (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (группа)
Типы данных в MPI MPI datatype C datatype MPI_CHAR signed char MPI_INT signed int MPI_LONG signed long int MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short int MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT float MPI_DOUBLE double MPI_LONG_DOUBLE long double
int MPI_Get_count (MPI_Status *status,MPI_Datatype datatype, int *count) - операция возвращает число полученных элементов IN status статус операции приема (статус ) IN datatype тип данных каждого элемента приемного буфера (дескриптор) OUT count количество полученных единиц (целое)
Модификации функции MPI_SEND: • MPI_BSEND(buf, count, datatype, dest, tag, comm)—передача сообщения с буферизацией. • MPI_SSEND (buf, count, datatype, dest, tag, comm)—передача сообщения с синхронизацией. • MPI_RSEND (buf, count, datatype, dest, tag, comm)—передача сообщения по готовности.
РАСПРЕДЕЛЕНИЕ И ИСПОЛЬЗОВАНИЕ БУФЕРОВ int MPI_Buffer_attach (void* buffer, int size) - описать буфера, используемого для буферизации сообщений, посылаемых в режиме буферизации. IN buffer начальный адрес буфера (альтернатива) IN size размер буфера в байтах (целое) int MPI_Buffer_detach(void* buffer_addr, int * size) - отключенние буфера OUT buffer_addr начальный адрес буфера (альтернатива) OUT size размер буфера в байтах (целое)
int MPI_Recv (void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status) - функция блокирующего приема OUT buf начальный адрес буфера процесса-получателя (альтернатива) IN count число элементов в принимаемом сообщении (целое) IN datatype тип данных каждого элемента сообщения (дескриптор) IN source номер процесса-отправителя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT status параметры принятого сообщения (статус)
Параметры принятого сообщения всегда можно определить по соответствующим элементам структуры STATUS: STATUS.MPI_SOURCE— номер процесса-отправителя. STATUS.MPI_TAG — идентификаторсообщения. STATUS.MPI_ERROR — код ошибки.
Вместо аргументов SOURCEи TAGможно использовать константы: • MPI_ANY_SOURCE— признак того, что подходит сообщение от любого процесса • MPI_ANY_TAG— признак того, что подходит сообщение с любым идентификатором.
Пример 2.1. «Все одному» #include "mpi.h" main (int argc, char **argv) { char message[20]; int myrank, size, i; MPI_Status status; MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD, &size); if (myrank==0) /* code for process zero */ { strcpy (message, "Hello, there"); for (i=1;i<size;i++) MPI_Send(message, strlen(message)+1, MPI_CHAR, i, 99, MPI_COMM_WORLD); } else /* code for process one */ { MPI_Recv (message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status); printf ("proc %d received :%s:\n",myrank, message); } MPI_Finalize(); }
Тупиковые ситуации (deadlock): процесс 0: RECV(1) SEND(1) процесс 1: RECV(0) SEND(0) процесс 0: SEND(1) RECV(1) процесс 1: SEND(0) RECV(0)
Разрешение тупиковых ситуаций: 1. процесс 0: SEND(1) RECV(1) процесс 1: RECV(0) SEND(0) 2. Использование неблокирующих операций (MPI_ISEND, MPI_IRECV) 3. Использование функции совмещенного обмена (MPI_SENDRECV)
#include "mpi.h" main(int argc, char **argv) { int me, size, n; int SOME_TAG=0; MPI_Status status; MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &me); MPI_Comm_size (MPI_COMM_WORLD, &size); if ((me % 2)==0) { /* send unless highest-numbered process */ if ((me+1) < size) { MPI_Send (&me, 1, MPI_INT,me+1,SOME_TAG,MPI_COMM_WORLD); MPI_Recv(&n,1,MPI_INT,me+1,SOME_TAG,MPI_COMM_WORLD,&status); printf("me %d received %d\n",me,n); } } else { MPI_Recv(&n,1,MPI_INT,me-1,SOME_TAG,MPI_COMM_WORLD,&status); MPI_Send (&me, 1, MPI_INT,me-1,SOME_TAG,MPI_COMM_WORLD); printf("me %d received %d\n",me,n); } MPI_Finalize(); } Пример 2.2: Парный обмен
int MPI_Isend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) - функция неблокирующей посылки IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN datatype тип каждого элемента в буфере посылки (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор)
int MPI_Irecv (void* buf, int count, MPI_Datatype datatype, int source, int tag,MPI_Comm comm, MPI_Request *request) - функция неблокирующего приема IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN datatype тип каждого элемента в буфере посылки (дескриптор) IN sourceномер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор)
Модификации функции MPI_ISEND: • MPI_IBSEND(buf, count, datatype, dest, tag, comm, request)—передача сообщения с буферизацией. • MPI_ISSEND (buf, count, datatype, dest, tag, comm, request)—передача сообщения с синхронизацией. • MPI_IRSEND (buf, count, datatype, dest, tag, comm, request)—передача сообщения по готовности.
Завершение обмена int MPI_Wait (MPI_Request *request, MPI_Status *status) - заканчивается, когда завершена операция, указанная в запросе. INOUT request запрос (дескриптор) OUT status объект состояния (статус) int MPI_Test (MPI_Request *request, int *flag, MPI_Status *status)- возвращает flag = true, если операция, указанная в запросе, завершена. INOUT request коммуникационный запрос (дескриптор) OUT flag true, если операция завершена (логический тип) OUT status статусный объект (статус)
Множественные завершения int MPI_Waitall (int count, MPI_Request *array_of_requests, MPI_Status *array_of_statuses)- блокирует работу, пока все операции обмена, связанные с активными дескрипторами в списке, не завершатся, и возвращает статус всех операций. IN count длина списков (целое) INOUT array_of_requests массив запросов (массив дескрипторов) OUT array_of_statuses массив статусных объектов (массив статусов) int MPI_Testall(int count, MPI_Request *array_of_requests, int *flag, MPI_Status *array_of_statuses) - возвращает flag=true, если обмены, связанные с активными дескрипторами в массиве, завершены. IN count длина списка (целое) INOUT array_of_requests массив запросов (массив дескрипторов) OUT flag (логический тип) OUT array_of_statuses массив статусных объектов(массив статусов)
Int MPI_Waitany (int count, MPI_Request *array_of_requests, int *index, MPI_Status *status)- блокирует работу до тех пор, пока не завершится одна из операций из массива активных запросов. Если более чем одна операция задействована и может закончиться, выполняется произвольный выбор. IN count длина списка (целое) INOUT array_of_requests массив запросов (массив дескрипторов) OUT index индекс дескриптора для завершенной операции (целое) OUT status статусный объект (статус) int MPI_Testany (int count, MPI_Request *array_of_requests, int *index, int *flag, MPI_Status *status) - тестирует завершение либо одной либо никакой из операций, связанных с активными дескрипторами. IN count длина списка (целое) INOUT array_of_requests массив запросов (массив дескрипторов) OUT index индекс дескриптора для завершенной операции (целое) OUT flag true, если одна из операций завершена (логический тип) OUT status статусный объект (статус)
int MPI_Waitsome (int incount, MPI_Request *array_of_requests, int *outcount, int *array_of_indices, MPI_Status *array_of_statuses) - ожидает, пока, по крайней мере, одна операция, связанная с активным дескриптором в списке, не завершится. IN incount длина массива запросов (целое) INOUT array_of_requests массив запросов (массив дескрипторов) OUT outcount число завершенных запросов (целое) OUT array_of_indices массив индексов операций, которые завершены (массив целых) OUT array_of_statuses массив статусных операций для завершенных операций (массив статусов) int MPI_Testsome (int incount, MPI_Request *array_of_requests, int *outcount, int *array_of_indices, MPI_Status *array_of_statuses) - ведет себя подобно MPI_WAITSOMEза исключением того, что заканчивается немедленно. IN incount длина массива запросов (целое) IN OUT array_of_requests массив запросов (массив дескрипторов) OUT outcount число завершенных запросов (целое) OUT array_of_indices массив индексов завершенных операций (массив целых) OUT array_of_statuses массив статусных объектов завершенных операций (массив статусов)
MPI_SUCCESS- удачное завершение обменов MPI_ERR_IN_STATUS - неудачное завершение обменов, устанавливается специфический код ошибки в поля ошибки каждого статуса. MPI_ERR_PENDING– обмен не завершен, но и не в состоянии отказа MPI_REQUEST_NULL – значение дескриптора (по завершении обмена он удаляется), если запрос был размещен вызовом неблокирующего обмена outcount = MPI_UNDEFINED - если не имеется активных дескрипторов в списке
Пример 3: Обмен по кольцу без блокировки #include "mpi.h" #include <stdio.h> 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("me %d recv %d %d\n",rank,buf[0],buf[1]); MPI_Finalize(); }
ПРОБА И ОТМЕНА int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status) - возвращает flag = true, если имеется сообщение, которое может быть получено и которое соответствует образцу, описанному аргументами source, tag, и comm. IN source номер процесса-отправителя или MPI_ANY_SOURCE (целое) IN tag значение тэга или MPI_ANY_TAG (целое) IN comm коммуникатор (дескриптор) OUT flag (логическое значение) OUT status статус (статус) int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status) –блокирующий аналогMPI_Iprobe IN source номер источника или MPI_ANY_SOURCE (целое) IN tag значение тэга или MPI_ANY_TAG (целое) IN comm коммуникатор (дескриптор) OUT status статус (статус)
int MPI_Cancel(MPI_Request *request) - маркирует для отмены ждущие неблокирующие операции обмена (передача или прием). IN request коммуникационный запрос (дескриптор) После маркировки необходимо завершить эту операцию обмена, используя вызов MPI_WAIT или MPI_TEST (или любые производные операции). int MPI_Test_cancelled(MPI_Status *status, int *flag) - возвращает flag = true, если обмен, связанный со статусным объектом, был отменен успешно. IN status статус (Status) OUT flag (логический тип )
int MPI_Sendrecv(void*sendbuf, int sendcount, MPI_Datatype sendtype, int dest,int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source,MPI_Datatype recvtag, MPI_Comm comm, MPI_Status *status) – функция совмещенного приема передачи (с блокировкой) IN sendbuf начальный адрес буфера отправителя (альтернатива) IN sendcount число элементов в буфере отправителя (целое) IN sendtype тип элементов в буфере отправителя (дескриптор) IN dest номер процесса-получателя (целое) IN sendtag тэг процесса-отправителя (целое) OUT recvbuf начальный адрес приемного буфера (альтернатива) IN recvcount число элементов в приемном буфере (целое) IN recvtype тип элементов в приемном буфере (дескриптор) IN source номер процесса-отправителя (целое) IN recvtag тэг процесса-получателя (целое) IN comm коммуникатор (дескриптор) OUT status статус (статус)
int MPI_Sendrecv_replace(void* buf,int count, MPI_Datatype datatype, int dest, int sendtag, int source, int recvtag, MPI_Comm comm, MPI_Status *status) - функция приема передачи с совмещенным буфером (с блокировкой) INOUT buf начальный адрес буфера отправителя и получателя (альтернатива) IN count число элементов в буфере отправителя и получателя (целое) IN datatype тип элементов в буфере отправителя и получателя (дескриптор) IN dest номер процесса-получателя (целое) IN sendtag тэг процесса-отправителя (целое) IN source номер процесса-отправителя (целое) IN recvtag тэг процесса-получателя (целое) IN comm коммуникатор (дескриптор) OUT status статус (статус)
Объединение запросов на взаимодействие int MPI_Send_init( void *buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request)- формирование запроса на выполнение пересылки данных INbuf - адрес начала буфера посылки сообщения INcount - число передаваемых элементов в сообщении INdatatype - тип передаваемых элементов INdest - номер процесса-получателя INmsgtag - идентификатор сообщения INcomm - идентификатор группы OUT request - идентификатор асинхронной передачи int MPI_Recv_init( void *buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Request *request)- формирование запроса на выполнение приема данных
MPI_Startall( int count, MPI_Request *requests) • запуск всех отложенных взаимодействий, ассоциированных с элементами массива запросовrequests. • IN count - число запросов на взаимодействие • OUT requests - массив идентификаторов приема/передачи • MPI_Start(MPI_Request *requests)
Пример 4. Вычисление числа где #include "mpi.h" #include <math.h> main ( int argc, char **argv ) { int n=0, myid, numprocs, i; double mypi, pi, h, sum, x, t1, t2, PI25DT = 3.141592653589793238462643; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); if (myid == 0) t1 = MPI_Wtime(); if (argc>1) n=atoi(argv[1]); else { printf("number of points is needed\n"); return; }
h = 1.0/ (double) n; sum = 0.0; for (i = myid +1; i <= n; i+= numprocs) { x = h * ( (double)i - 0.5); sum += (4.0 / (1.0 + x*x)); } mypi = h * sum; // Cуммирования mypi со всех процессоров в переменную pi MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) { t2 = MPI_Wtime(); printf ("pi is approximately %.16f. Error is %.16f\n",pi, fabs(pi - PI25DT)); printf ("'time is %f seconds \n", t2-t1); } MPI_Finalize(); }