Скачать презентацию
Идет загрузка презентации. Пожалуйста, подождите
Презентация была опубликована 11 лет назад пользователемТарас Трошин
1 Библиотека MPI Message Passing Interface
2 История MPI Стандарт MPI год, MPI год. Определяет API (варианты для Си, C++, Fortran, Java).
3 «Комплект поставки» MPI Библиотека. Средства компиляции и запуска приложения.
4 SPMD-модель Разные процессы выполняют разные части одного и того же кода.
5 Сборка MPI-приложения. Сборка MPI-приложения осуществляется с помощью специальной утилиты. В случае Си – mpicc. Пример: mpicc –o mpihello mpihello.c Запуск MPI-приложения осуществляется с помощью команды mpirun. mpirun –np 4 mpihello
6 MPI Hello, World #include main(int argc, char* argv[]) { MPI_Init(&argc, &argv); printf("Hello, World!\n"); MPI_Finalize(); }
7 Функции инициализации и завершения работы. int MPI_Init(int* argc,char*** argv) argc – указатель на счетчик аргументов командной строки argv – указатель на список аргументов int MPI_Finalize()
8 Тоже простая MPI-программа #include int main( int argc, char *argv[] ) { int rank, size; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &rank ); MPI_Comm_size( MPI_COMM_WORLD, &size ); printf( "I am %d of %d\n", rank, size ); MPI_Finalize(); return 0; }
9 Функции определения ранга и числа процессов. int MPI_Comm_size (MPI_Comm comm, int* size ) comm - коммуникатор size – число процессов int MPI_Comm_rank(MPI_Comm comm, int* rank) comm – коммуникатор rank – ранг процесса
10 Точечные взаимодействия
11 Назначение точечных взаимодействий
12 Пример простейшей пересылки #include main(int argc, char* argv[]) { int rank; MPI_Status st; char buf[64]; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if(rank == 0) { sprintf(buf, "Hello from processor 0"); MPI_Send(buf, 64, MPI_CHAR, 1, 0, MPI_COMM_WORLD); } else { MPI_Recv(buf, 64, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &st); printf("Process %d received %s \n", rank, buf); } MPI_Finalize(); }
13 Функции обменов точка-точка int MPI_Send( buf, count, datatype, dest, tag, comm ) void *buf; /* in */ int count, dest, tag; /* in */ MPI_Datatype datatype; /* in */ MPI_Comm comm; /* in */ buf - адрес начала буфера посылаемых данных count - число пересылаемых объектов типа, соответствующего datatype dest - номер процесса-приемника tag - уникальный тэг, идентифицирующий сообщение datatype - MPI-тип принимаемых данных comm - коммуникатор
14 int MPI_Recv( buf, count, datatype, source, tag, comm, status ) void *buf; /* in */ int count, source, tag; /* in */ MPI_Datatype datatype; /* in */ MPI_Comm comm; /* in */ MPI_Status *status; /* out */ buf - адрес буфера для приема сообщения count - максимальное число объектов типа datatype, которое может быть записано в буфер source - номер процесса, от которого ожидается сообщение tag - уникальный тэг, идентифицирующий сообщение datatype - MPI-тип принимаемых данных comm - коммуникатор status - статус завершения
15 typedef struct { int count; int MPI_SOURCE; int MPI_TAG; int MPI_ERROR; } MPI_Status; count - число полученных элементов MPI_SOURCE - ранг процесса-передатчика данных MPI_TAG - тэг сообщения MPI_ERROR - код ошибки
16 Численное интегрирование
17 MPI-программа численного интегрирования #include double f(double x) { return 4./(1 + x * x); } main(int argc, char* argv[]) { int r; int p; int i; double sum; double h; MPI_Status st; double t; int n = ; double a = 0.0; double b = 1.0;
18 MPI-программа численного интегрирования MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &r); MPI_Comm_size(MPI_COMM_WORLD, &p); if(r == 0) t = MPI_Wtime(); MPI_Barrier(MPI_COMM_WORLD); sum = 0.0; h = (b - a) / n; for(i = r; i < n; i += p) sum += f(a + (i + 0.5) * h); sum *= h; MPI_Send(&sum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); sum = 0;
19 MPI-программа численного интегрирования if(r == 0) { double s; for(i = 0; i < p; i ++) { MPI_Recv(&s, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &st); sum += s; } t = MPI_Wtime() - t; printf("Integral value = %lf. Time = %lf sec.\n", sum, t); } MPI_Finalize(); }
20 Результаты вычислительного эксперимента Данные получены на MVS-15000BM.
21 Семантика точечных взаимодействий нет буферизации системный буфер буфер пользователя
22 Виды точечных взаимодействий MPI_Send блокирующая пересылка функция возвращает управление тогда, когда исходный буфер можно освобождать (т.е. данные или скопированы в промежуточный или отправлены) MPI_Bsendбуферизованная пересылка функция возвращает управление тогда, когда данные скопированы в буфер, выделяемый пользователем
23 MPI_Ssendсинхронная пересылка функция возвращает управление тогда, когда процесс-приемник преступил к выполнению соответствующей операции приема MPI_Rsend интерактивная пересылка поведение функции не определено, если соответствующая операция приема не начала выполнения (для увеличения производительности)
24 Буферизованная пересылка Процесс- отправитель выделяет буфер и регистрирует его в системе. Функция MPI_Bsend помещает данные выделенный буфер,.
25 Буферизованная пересылка int MPI_Bsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) Завершается после копирования данных из буфера buf в буфер для отсылаемых сообщений, выделенный программой. Если места в буфере недостаточно, то возвращается ошибка.
26 Функции работы с буфером обмена int MPI_Buffer_attach( buffer, size ) void *buffer; /* in */ int size; /* in */ buffer - адрес начала буфера size - размер буфера в байтах int MPI_Buffer_detach( bufferptr, size ) void *bufferptr; /* out */ int *size; /* out */ *bufferptr - адрес высвобожденного буфера *size - размер высвобожденного пространства функция MPI_Buffer_detach блокирует процесс до тех пор, пока все данные не отправлены из буфера
27 Вычисление размера буфера int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size) Вычисляет размер памяти для хранения одного сообщения. MPI_BSEND_OVERHEAD – дополнительный объем для хранения служебной информации (организация списка сообщений). Размер буфера для хранения n одинаковых сообщений вычисляется по формуле: n x (размер_одного_сообщения + MPI_BSEND_OVERHEAD)
28 Порядок организации буферизованных пересылок Вычислить необходимый объем буфера (MPI_Pack_size). Выделить память под буфер (malloc). Зарегистрировать буфер в системе (MPI_Buffer_attach). Выполнить пересылки. Отменить регистрацию буфера (MPI_Buffer_dettach) Освободить память, выделенную под буфер (free).
29 Особенности работы с буфером Буфер всегда один. Для изменения размера буфера сначала следует отменить регистрацию, затем увеличить размер буфера и снова его зарегистрировать. Освобождать буфер следует только после того, как отменена регистрация.
30 MPI_Pack_size(1, MPI_INT, MPI_COMM_WORLD,&msize) blen = M * (msize + MPI_BSEND_OVERHEAD); buf = (int*) malloc(blen); MPI_Buffer_attach(buf, blen); for(i = 0; i < M; i ++) { n = i; MPI_Bsend(&n, 1, MPI_INT, 1, i, MPI_COMM_WORLD); } MPI_Buffer_detach(&abuf, &ablen); free(abuf); Пример буферизованной пересылки
31 Неблокирующие пересылки Предназначены для перекрытия обменов и вычислений. Операция расщепляется на две: инициация и завершение.
32 Неблокирующая пересылка int MPI_Isend( buf, count, datatype, dest, tag, comm, request) MPI_Request *request; /* out */ MPI_Ibsend(…), MPI_Issend(…), MPI_Irsend(…) int MPI_Irecv( buf, count, datatype, source, tag, comm, request ) MPI_Request *request; /* out */ Инициация:
33 Завершение: int MPI_Wait (MPI_Request * request, MPI_Status * status) int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status) int MPI_Waitall(int count, MPI_Request array_of_requests[], MPI_Status array_of_statuses[] ) int MPI_Waitany(int count, MPI_Request array_of_requests[], int* index, MPI_Status *status )
34 Пример: кольцевой сдвиг данных
35 #include "mpi.h" #include int main (argc, argv) 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 == 0) ? (numtasks - 1) : (rank - 1); next = (rank == (numtasks - 1)) ? 0 : (rank + 1);
36 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("rank: %d, buf[0]: %d, buf[1]: %d\n", rank, buf[0], buf[1]); MPI_Finalize (); }
37 Прием по шаблону В качестве параметров source и tag в функции MPI_Recv могут быть использованы константы MPI_ANY_SOURCE и MPI_ANY_TAG соответственно. Допускается прием от процесса с произвольным номером и/или сообщения с произвольным тэгом.
38 Стратегия управляющий- рабочие
39 Адаптивная квадратура Частота разбиения выбирается в соответствии с плавностью изменения функции.
40 #include #include #define MYABS(A) (((A) < 0) ? (-(A)) : (A)) double f(double x) { return sin(1. / x); } ПРИМЕР РЕАЛИЗАЦИИ
41 int adint(double (*f) (double), double left, double right, double eps, double *nint) { double mid; double h; double Iold; double Ileft; double Iright; h = 0.5 * (right - left); mid = 0.5 * (right + left); Iold = h * (f(left) + f(right)); Ileft = 0.5 * h * (f(left) + f(mid)); Iright = 0.5 * h * (f(mid) + f(right)); *nint = Ileft + Iright; if(MYABS(Iold - *nint) < eps) return 1; else return 0; }
42 double recadint(double (*f)(double), double left, double right, double eps) { double I; if(adint(f, left, right, eps, &I)) { return I; } else { double Ileft; double Iright; double mid; mid = 0.5 * (right + left); Ileft = recadint(f, left, mid, 0.5 * eps); Iright = recadint(f, mid, right, 0.5 * eps); return Ileft + Iright; } }
43 main(int argc, char* argv[]) { int r, p, n = , s = 0; double a = , b = 1.0, eps = , I = 0., h, t; MPI_Status st; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &r); MPI_Comm_size(MPI_COMM_WORLD, &p); h = (b - a) / n; n --; t = MPI_Wtime();
44 if(r == 0) { while(s != (p-1)) { double Islave; MPI_Recv(&Islave, 1, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &st); I += Islave; MPI_Send(&n, 1, MPI_INT, st.MPI_SOURCE, 0, MPI_COMM_WORLD); if(n >= 0) n --; else s += 1; } } else { int m; while(1) { MPI_Send(&I, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); MPI_Recv(&m, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &st); if(m >= 0) I = recadint(f, a + h * m, a + h * (m + 1), eps * h / (b - a)); else break; } }
45 if(r == 0) { t = MPI_Wtime() - t; printf("Integral value: %lf, time = %lf\n", I, t); } MPI_Finalize(); }
46 Результаты экспериментов данные получены на MVS BM
47 Deadlock if(rank == 0) { MPI_Ssend(… 1 …) MPI_Recv(…1…) } else { MPI_Ssend(… 0 …) MPI_Recv(…0…) }
48 «Недетерминированный» deadlock if(rank == 0) { MPI_Send(… 1 …) MPI_Recv(…1…) } else { MPI_Send(… 0 …) MPI_Recv(…0…) }
49 Недетерминизм за счет разницы в относительных скоростях процессов (race condition)
50 Коллективные взаимодействия процессов
51 MPI предоставляет ряд функций для коллективного взаимодейстия процессов. Эти функции называют коллективными, поскольку они должны вызываться на всех процессах, принадлежащих некоторому коммуникатору.
52 int MPI_Bcast ( buffer, count, datatype, root, comm ) void* buffer - начальный адрес буфера для передачи собщений int count - число передаваемых элементов данных MPI_Datatype datatype - тип передаваемых данных int root - ранг процесса, посылающего данные MPI_Comm comm - коммуникатор
53 int MPI_Reduce ( sendbuf, recvbuf, count, datatype, op, root, comm ) void *sendbuf; буфер операндов void *recvbuf; буфер приема int count; число данных MPI_Datatype datatype; тип данных MPI_Op op; операция int root; ранг процесса, содержащего результат MPI_Comm comm; коммуникатор op
54 MPI_MAX максимум MPI_MIN минимум MPI_SUM сумма MPI_PROD произведение MPI_LAND логическое "и" MPI_BAND побитовое "и" MPI_LOR логическое "или" MPI_BOR побитовое "или" MPI_LXOR логическое исключающее "или" MPI_BXOR побитовое исключающее "или"
55 Вычисление числа Пи
56 Вычисление числа Pi #include "mpi.h" #include int main(argc,argv) int argc; char *argv[]; { int n, myid, numprocs, i; double PI25DT = ; double mypi, pi, h, sum, x, a; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid);
57 while (1) { if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); } MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD); if (n == 0) break; h = 1.0 / (double) n; sum = 0.0; for (i = myid + 1; i
58 MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - PI25DT)); } MPI_Finalize(); }
59 Функция синхронизации процессов: int MPI_Barrier ( comm ) ; MPI_Comm comm;
60 int MPI_Scatter ( sendbuf, sendcnt, sendtype, recvbuf, recvcnt, recvtype, root, comm ) void *sendbuf; int sendcnt; MPI_Datatype sendtype; void *recvbuf; int recvcnt; MPI_Datatype recvtype; int root; MPI_Comm comm;
61 int MPI_Gather ( sendbuf, sendcnt, sendtype, recvbuf, recvcount, recvtype, root, comm ) void *sendbuf; int sendcnt; MPI_Datatype sendtype; void *recvbuf; int recvcount; MPI_Datatype recvtype; int root; MPI_Comm comm;
62 int MPI_Allreduce ( sendbuf, recvbuf, count, datatype, op, comm ) void *sendbuf; void *recvbuf; int count; MPI_Datatype datatype; MPI_Op op; MPI_Comm comm; int MPI_Allgather ( sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm ) void *sendbuf; int sendcount; MPI_Datatype sendtype; void *recvbuf; int recvcount; MPI_Datatype recvtype; MPI_Comm comm;
63 int MPI_Alltoall( sendbuf, sendcount, sendtype, recvbuf, recvcnt, recvtype, comm ) void *sendbuf; int sendcount; MPI_Datatype sendtype; void *recvbuf; int recvcnt; MPI_Datatype recvtype; MPI_Comm comm;
64 Метод Якоби решения линейных систем, -
65 Условие остановки:
66 Матричная форма записи
67 Условие сходимости Диагональное преобладание – достаточное условие сходимости. Пример:
68 Последовательный алгоритм #define MAXITERS 1000 void init(int N, double* A, double* b, double* cold) { int i, j; for(i = 0; i < N; i ++) b[i] = ((double)N + 1)/4. + ( /(double)(2 * N)) * (i + 1); for(i = 0; i < N; i ++) { for(j = 0; j < N; j ++) A[i * N + j] = 1./(double)(2 * N); } for(i = 0; i < N; i ++) A[i * N + i] = 0.0; for(i = 0; i < N; i ++) cold[i] = 1.; }
69 double evalDiff(double* u, double* v, int N){ int i; double a = 0.0; for(i = 0; i < N; i ++) { double b; b = v[i] - u[i]; a += b * b; } return a; } void printVec(double* v, int n){ int i; for(i = 0; i < n; i ++) printf("%lf ", v[i]); printf("\n"); }
70 main(int argc, char* argv[]){ int N, I = 0; double *A, *b, *c, *cold, eps, diff, t; N = atoi(argv[1]); eps = atof(argv[2]); A = (double*)malloc(N * N * sizeof(double)); b = (double*)malloc(N * sizeof(double)); cold = (double*)malloc(N * sizeof(double)); c = (double*)malloc(N * sizeof(double)); init(N, A, b, cold); t = time(NULL); do { int i; for(i = 0; i < N; i ++) { int j; double a = 0.; double* row = A + i * N; for(j = 0; j < N; j ++) a += row[j] * cold[j]; c[i] = -a + b[i]; }
71 diff = evalDiff(cold, c, N); I ++; printf("diff = %lf, eps = %lf\n", diff, eps); memcpy(cold, c, N * sizeof(double)); } while ((diff >= eps) && (I = eps) && (I ">
72 Параллельный алгоритм A c old c цикл
73 main(int argc, char* argv[]) { int M, N, np, rk, chunk, mychunk, i, I = 0; double *A, *Aloc, *b = NULL, *c, *cloc, *cold, t, diff, eps; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &np); MPI_Comm_rank(MPI_COMM_WORLD, &rk); if(rk == 0) { N = atoi(argv[1]); eps = atof(argv[2]); M = np * (N / np) + ((N % np) ? np : 0); chunk = M / np; A = (double*)malloc(M * N * sizeof(double)); } MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Bcast(&eps, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Bcast(&chunk, 1, MPI_INT, 0, MPI_COMM_WORLD);
74 b = (double*)malloc(N * sizeof(double)); c = (double*)malloc(N * sizeof(double)); cold = (double*)malloc(N * sizeof(double)); Aloc = (double*)malloc(chunk * N * sizeof(double)); cloc = (double*)malloc(chunk * sizeof(double)); if(rk == 0){ init(M, N, A, b, cold); t = MPI_Wtime(); } MPI_Scatter(A, N * chunk, MPI_DOUBLE, Aloc, N * chunk, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Bcast(b, N, MPI_DOUBLE, 0, MPI_COMM_WORLD); mychunk = ((rk == (np - 1)) ? N - (np - 1) * chunk : chunk);
75 do { MPI_Bcast(cold, N, MPI_DOUBLE, 0, MPI_COMM_WORLD); for(i = 0; i = eps) && (I
76 if(rk == 0) { t = MPI_Wtime() - t; printf("%d iterations consumed %lf sec\n", I, t); } MPI_Finalize(); }
77 Группы и коммуникаторы
78 Интер- и интра- коммуникаторы Интра- коммуникаторы Интер- коммуникатор Интра-коммуникаторы объединяют процессы из одной группы. Интер-коммуникатор позволяет передавать данные между процессами из разных интра-коммуникаторов. Интер-коммуникаторы не могут использоваться в коллективных взаимодействиях
79 Назначение коммуникаторов Поддержка параллельных библиотек. Поддержка коллективных операций на части вычислительного пространства. Повышение уровня абстракции параллельных приложений.
80 #include "mpi.h" #include double g(double x) { return 4.0 / (1.0 + x * x); } void quad(int n, double (*f) (double), MPI_Comm comm, double* result) { int myid, numprocs, i; double h, sum, x, mypi; MPI_Comm_rank(comm,&myid); MPI_Comm_size(comm, &numprocs); MPI_Bcast(&n, 1, MPI_INT, 0, comm); h = 1.0 / (double) n; sum = 0.0; for (i = myid + 1; i
81 int main(int argc, char *argv[]) { int n, myid; double PI25DT = ; double pi; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD,&myid); while (1) { if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); if(n == 0) MPI_Abort(MPI_COMM_WORLD, -1); } quad(n, g, MPI_COMM_WORLD, &pi); if (myid == 0) printf("pi is approximately %.16f, Error is %.16f\n", pi, fabs(pi - PI25DT)); } MPI_Finalize(); }
82 Создание коммуникаторов Разбиение коммуникатора на несколько: int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm* newcomm) comm – «старый коммуникатор» color – селектор коммуникатора key – задает порядок на создаваемых коммуникаторах newcomm – создаваемый коммуникатор color >= 0 для color = MPI_UNDEFINED будет создан коммуникатор MPI_COMM_NULL ранги во вновь создаваемых коммуникаторах присваиваются в соответствии с возрастанием key
83 int main(int argc, char **argv) { int n, myid; double PI25DT = ; double pi, res; MPI_Comm comm; MPI_Comm rcomm; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Comm_split(MPI_COMM_WORLD, myid % 2, 0, &comm); MPI_Comm_split(MPI_COMM_WORLD, (myid < 2) ? 1 : MPI_UNDEFINED, 0, &rcomm); while (1) { if (myid == 0) { printf("Enter the number of intervals: (0 quits) "); scanf("%d",&n); if(n == 0) MPI_Abort(MPI_COMM_WORLD, -1); } if(myid < 2) MPI_Bcast(&n, 1, MPI_INT, 0, rcomm);
84 if(myid % 2) quad(n, g, comm, &pi); else quad(n, h, comm, &pi); if(myid < 2) MPI_Reduce(&pi, &res, 1, MPI_DOUBLE, MPI_SUM, 0, rcomm); if (myid == 0) printf("result is %f\n", res); } MPI_Finalize(); }
85 Группы и коммуникаторы Совокупности MPI-процессов образуют группы. Понятие ранга процесса имеет смысл только по отношению к определенной группе или коммуникатору. Каждому интра-коммуникатору соответствует группа процессов. По группе процессов можно построить коммуникатор.
86 Получение коммуникатора по группе: int MPI_Comm_group(MPI_Comm comm, MPI_Group *group) comm – коммуникатор; group – указатель на область памяти для сохранения полученной группы;
87 Объединение двух групп: int MPI_Group_union(MPI_Group gr1, MPI_Group g2, MPI_Group* gr3) gr1 – первая группа; gr2 – вторая группа; gr3 – указатель на область для сохранения результата операции; Набор номеров gr3 получается конкатенацией наборов номеров gr1 и gr2, причем элементы gr2 следуют за элементами gr1. Пересечение двух групп: int MPI_Group_intersection(MPI_Group gr1, MPI_Group g2, MPI_Group* gr3) gr1 – первая группа; gr2 – вторая группа; gr3 – указатель на область для сохранения результата операции; Набор номеров gr3 составлен из номеров, входящих в gr1 и в gr2, расположенных в том же порядке, что и в gr1.
88 Разность двух групп: int MPI_Group_difference(MPI_Group gr1, MPI_Group g2, MPI_Group* gr3) gr1 – первая группа; gr2 – вторая группа; gr3 – указатель на область для сохранения результата операции; Набор номеров gr3 составлен из номеров, входящих в gr1, но не входящих в gr2, расположенных в том же порядке, что и в gr1.
89 Переупорядочивание (с возможным удалением) процессов в существующей группе: int MPI_Group_incl(MPI_Group* group, int n, int* ranks, MPI_Group* newgroup) group – исходная группа; n – число элементов в массиве ranks; ranks – массив номеров процессов, из которых будет создана новая группа; newgroup – указатель на область для сохранения результата операции; Созданная группа newgroup содержит элементы группы group, перечисленные в массиве ranks: i-й элемент создаваемой группы newgroup совпадает с ranks[i].
90 Удаление процессов из группы: int MPI_Group_excl(MPI_Group* group, int n, int* ranks, MPI_Group* newgroup) group – исходная группа; n – число элементов в массиве ranks; ranks – массив номеров удаляемых процессов; newgroup – указатель на область для сохранения результата операции; В результате выполнения этой операции создается новая группа newgroup, получаемая удалением из исходной группы процессов с номерами, перечисленными в массиве ranks. Освобождение памяти, отведенной для группы: int MPI_Group_free(MPI_Group* group) group – идентификатор освобождаемой группы.
91 Дублирование коммуникатора Получение дубликата коммуникатора: int MPI_Comm_dup(MPI_Comm comm, MPI_Comm* newcomm) Используется для того, чтобы снабдить библиотечную новым коммуникатором, совпадающим по характеристикам со старым, но вместе с тем, создающим новый контекст для коммуникаций. Цель: исключить проблемы, связанные с «перемешиванием коммуникаций».
92 Создание коммуникатора по группе и удаление Создание коммуникатора по группе процессов: int MPI_Comm_create(MPI_Comm comm,MPI_Group group, MPI_Comm *newcomm) comm – исходный коммуникатор; group – группа, по которой создается коммуникатор; newcomm – область для сохранения результата операции; В результате создается новый коммуникатор, объединяющий процессы из этой группы group. Результат операции сохраняется в область памяти, указатель на которую передается в качестве третьего аргумента. Функция MPI_Comm_create должна вызываться на всех процессах, входящих в исходный коммуникатор.
93 Удаление коммуникатора Освобождение коммуникатора: int MPI_Comm_free(MPI_Comm *comm) При освобождении коммуникатора все незавершенные операции будут завершены, только после этого коммуникатор будет удален физически.
94 Вычисление числа Pi методом Монте-Карло Из книги Gropp, Lusk, Skjellum
95 Схема вычислений server worker processes коммуникатор workers коммуникатор world
96 /* compute pi using Monte Carlo method */ #include #include "mpi.h" #define CHUNKSIZE 1000 #define INT_MAX /* message tags */ #define REQUEST 1 #define REPLY 2 int main( int argc, char *argv[] ) { int iter; int in, out, i, iters, max, ix, iy, ranks[1], done, temp; double x, y, Pi, error, epsilon; int numprocs, myid, server, totalin, totalout, workerid; int rands[CHUNKSIZE], request; MPI_Comm world, workers; MPI_Group world_group, worker_group; MPI_Status status; MPI_Init(&argc,&argv); world = MPI_COMM_WORLD; MPI_Comm_size(world,&numprocs); MPI_Comm_rank(world,&myid);
97 server = numprocs-1; if (myid == 0) sscanf( argv[1], "%lf", &epsilon ); MPI_Bcast( &epsilon, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD ); MPI_Comm_group( world, &world_group ); ranks[0] = server; MPI_Group_excl( world_group, 1, ranks, &worker_group ); MPI_Comm_create( world, worker_group, &workers ); MPI_Group_free(&worker_group); if (myid == server) {/* I am the rand server */ do { MPI_Recv(&request, 1, MPI_INT, MPI_ANY_SOURCE, REQUEST, world, &status); if (request) { for (i = 0; i < CHUNKSIZE; i++) rands[i] = random(); MPI_Send(rands, CHUNKSIZE, MPI_INT, status.MPI_SOURCE, REPLY, world); } } while( request>0 ); } 0 ); }">
98 else {/* I am a worker process */ request = 1; done = in = out = 0; max = INT_MAX; /* max int, for normalization */ MPI_Send( &request, 1, MPI_INT, server, REQUEST, world ); MPI_Comm_rank( workers, &workerid ); iter = 0; while (!done) { iter++; request = 1; MPI_Recv( rands, CHUNKSIZE, MPI_INT, server, REPLY, world, &status ); for (i=0; i
99 MPI_Allreduce(&in, &totalin, 1, MPI_INT, MPI_SUM, workers); MPI_Allreduce(&out, &totalout, 1, MPI_INT, MPI_SUM, workers); Pi = (4.0*totalin)/(totalin + totalout); error = fabs( Pi ); done = (error ); request = (done) ? 0 : 1; if (myid == 0) { printf( "\rpi = %23.20f", Pi ); MPI_Send( &request, 1, MPI_INT, server, REQUEST, world ); } else { if (request) MPI_Send(&request, 1, MPI_INT, server, REQUEST, world); }
100 if (myid == 0) { printf( "\npoints: %d\nin: %d, out: %d, to exit\n", totalin+totalout, totalin, totalout ); getchar(); } MPI_Comm_free(&workers); MPI_Finalize(); }
101 Система типов сообщений MPI
102 Типы в MPI БАЗОВЫЕ ТИПЫ MPI_CHAR MPI_SHORT MPI_INT MPI_LONG MPI_UNSIGNED_CHAR MPI_UNSIGNED_SHORT MPI_UNSIGNED MPI_UNSIGNED_LONG MPI_FLOAT MPI_DOUBLE MPI_LONG_DOUBLE ПРОИЗВОДНЫЕ ТИПЫ MPI_TYPE_CONTIGUOUS MPI_TYPE_VECTOR MPI_TYPE_HVECTOR MPI_TYPE_INDEXED MPI_TYPE_HINDEXED MPI_TYPE_STRUCT MPI_PACKED
103 БАЗОВЫЕ ТИПЫ тип MPIтип Си MPI_CHARsigned char MPI_SHORTsigned short int MPI_INTsigned int MPI_LONGsigned long int MPI_UNSIGNED_CHARunsigned char MPI_UNSIGNED_SHORTunsigned short MPI_UNSIGNEDunsigned int MPI_UNSIGNED_LONGunsigned long int MPI_FLOATfloat MPI_DOUBLEdouble MPI_LONG_DOUBLElong double
104 ПРОИЗВОДНЫЕ ТИПЫ MPI_TYPE_CONTIGUOUS – массив «без дырок»; MPI_TYPE_VECTOR, MPI_TYPE_HVECTOR – регулярно (с постоянным шагом) расположенные в памяти блоки однотипных элементов; MPI_TYPE_INDEXED,MPI_TYPE_HINDEXED – произвольно расположенные блоки однотипных элементов; MPI_TYPE_STRUCT – произвольно расположенные в памяти блоки элементов произвольных типов; MPI_TYPE_STRUCT MPI_TYPE_VECTOR MPI_TYPE_HVECTOR MPI_TYPE_INDEXED MPI_TYPE_HINDEXED MPI_TYPE_CONTIGUOUS
105 Назначение производных типов пересылка данных, расположенных в несмежных областях памяти в одном сообщении; пересылка разнотипных данных в одном сообщении; облегчение понимания программы;
106 MPI_TYPE_CONTIGUOUS MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype *newtype) oldtype... count
107 MPI_TYPE_VECTOR MPI_Type_vector(int count, int blocksize, int stride, MPI_Datatype oldtype, MPI_Datatype *newtype) oldtype... count (число блоков) oldtype stride blocksize
108 Отрицательный шаг oldtype stride = -4 oldtype stride =
109 MPI_TYPE_INDEXED MPI_Type_indexed(int count, int* blocksizes, int* displacements, MPI_Datatype oldtype, MPI_Datatype *newtype) oldtype count (число блоков) oldtype blocksizes[0] blocksizes[1] displacements[1]
110 MPI_TYPE_HVECTOR MPI_TYPE_HINDEXED основное отличие: смещение задается в байтах – необходимо знать точные значения размеров и требований выравнивания для типов в конкретной архитектуре;
111 MPI_TYPE_STRUCT MPI_Type_struct(int count, int* blocksizes, int* displacements, MPI_Datatype *types, MPI_Datatype *newtype) types[0] types[1] count (число блоков) types[0] types[1] blocksizes[0] blocksizes[1] displacements[1]
112 РЕГИСТРАЦИЯ ТИПА Регистрация типа: int MPI_Type_commit(MPI_Datatype *datatype) Освобождение памяти: int MPI_Type_free(MPI_Datatype *datatype)
113 ПОРЯДОК РАБОТЫ С ПРОИЗВОДНЫМИ ТИПАМИ Создание типа с помощью конструктора. Регистрация. Использование. Освобождение памяти.
114 КАРТА И СИГНАТУРА ТИПА Карта типа - набор пар (базовый тип, смещение): ((type 1, disp 1 ), (type 2, disp 2 ),..., (type n, disp n )), соответствующая сигнатура типа – набор базовых типов: (type 1, type 2,..., type n ).
115 СООТВЕТСТВИЕ ТИПОВ Соответствие типов отправителя и получателя: Сигнатура типа пришедшего сообщения является начальной подпоследовательностью сигнатуры типа, указанного в операции приема.
116 ПРИМЕРЫ send: MPI_TYPE_VECTOR(3, 1, 2, MPI_DOUBLE) recv: MPI_TYPE_CONTIGUOUS(3, MPI_DOUBLE) double send:MPI_TYPE_CONTIGUOUS(6, MPI_DOUBLE) recv: MPI_TYPE_VECTOR(3, 1, 2, MPI_DOUBLE) double
117 ПРИМЕРЫ send: MPI_TYPE_VECTOR(3, 1, 2, MPI_DOUBLE) recv: MPI_TYPE_CONTIGUOUS(6, MPI_DOUBLE) double send: MPI_TYPE_CONTIGUOUS(6, MPI_DOUBLE) recv:MPI_TYPE_VECTOR(3, 1, 2, MPI_DOUBLE) double
118 ПРИМЕРЫ MPI_TYPE_HVECTOR(3, 1, 2, MPI_DOUBLE) MPI_TYPE_VECTOR(3, 1, 2, MPI_DOUBLE) double MPI_TYPE_VECTOR(3, 1, 2, MPI_DOUBLE) MPI_TYPE_HVECTOR(3, 1, 2, MPI_DOUBLE) double недопустимо на стороне приема
119 ТРАНСПОНИРОВАНИЕ МАТРИЦЫ AATAT Процесс #1Процесс #2
120 #include #define N 3 int A[N][N]; void fill_matrix() { int i,j; for(i = 0; i < N; i ++) for(j = 0; j < N; j ++) A[i][j] = i * N + j; } void print_matrix() { int i,j; for(i = 0; i < N; i ++) { for(j = 0; j < N; j ++) printf("%d ", A[i][j]); printf("\n"); }
121 main(int argc, char* argv[]) { int r, i; MPI_Status st; MPI_Datatype typ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &r); if(r == 0) { fill_matrix(); printf("\n Source:\n"); print_matrix(); MPI_Type_contiguous(N, MPI_INT, &typ); MPI_Type_commit(&typ); MPI_Barrier(MPI_COMM_WORLD); for(i = 0; i < N; i ++) MPI_Send(&(A[i][0]), 1, typ, 1, 0, MPI_COMM_WORLD); }
122 else if(r == 1){ MPI_Type_vector(N, 1, N, MPI_INT, &typ); MPI_Type_commit(&typ); MPI_Barrier(MPI_COMM_WORLD); for(i = 0; i < N; i ++) MPI_Recv(&(A[0][i]), 1, typ, 0, 0, MPI_COMM_WORLD, &st); printf("\n Transposed:\n"); print_matrix(); } MPI_Type_free(&typ); MPI_Finalize(); }
123 РЕЗУЛЬТАТ РАБОТЫ Transposed: Source:
124 if(r == 0) { fill_matrix(); printf("\n Source:\n"); print_matrix(); MPI_Type_contiguous(N * N, MPI_INT, &typ); MPI_Type_commit(&typ); MPI_Barrier(MPI_COMM_WORLD); MPI_Send(&(A[0][0]), 1, typ, 1, 0, MPI_COMM_WORLD); } else if(r == 1){ MPI_Type_vector(N, 1, N, MPI_INT, &typ); MPI_Type_hvector(N, 1, sizeof(int), typ, &typ1); MPI_Type_commit(&typ); MPI_Barrier(MPI_COMM_WORLD); MPI_Recv(&(A[0][0]), 1, typ1, 0, 0, MPI_COMM_WORLD, &st); printf("\n Transposed:\n"); print_matrix(); } ОДНА ПЕРЕСЫЛКА
125 УПАКОВКА СООБЩЕНИЙ дает возможность пересылать разнородные данные в одном сообщении; отделяет операцию формирования сообщения от операции пересылки; способствует развитию библиотек на базе MPI;
126 MPI_PACK int MPI_Pack(void* inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outcount, int *position,, MPI_Comm comm) inbuf – буфер с данными для запаковки; incount – число элементов для запаковки; datatype – тип элементов данных; outbuf – буфер сообщения; outsize – размер буфера сообщения; position – позиция в буфере сообщения, с которой заполнять буфер (изменяется); comm – коммуникатор, по которому сообщение будет посылаться;
127 MPI_UNPACK int MPI_Unpack(void* inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm) inbuf – буфер сообщения; incount – число элементов данных для распаковки position – позиция, с которой распаковывать данные (изменяется); outbuf – буфер для распаковки; outcount – число элементов для распаковки; datatype – тип элементов данных; comm – коммуникатор, по которому сообщение будет посылаться;
128 ОПРЕДЕЛЕНИЕ РАЗМЕРА СООБЩЕНИЯ int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size) incount – число элементов данных в сообщении; datatype – тип элементов данных; comm – коммуникатор; size – размер сообщения;
129 ПРИМЕР #include #define N 3 main(int argc, char* argv[]) { int r; int i; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &r);
130 ПРИМЕР if(r == 0){ int sz; int pos = 0; int a = 1; void* buf; MPI_Pack_size(N, MPI_INT, MPI_COMM_WORLD, &sz); buf = (void*) malloc(sz); for(i = 0; i < N; i ++) { MPI_Pack(&a, 1, MPI_INT, buf, sz, &pos, MPI_COMM_WORLD); a ++; } MPI_Send(buf, pos, MPI_PACKED, 1, 0, MPI_COMM_WORLD); }
131 ПРИМЕР else { MPI_Status st; int A[N]; MPI_Recv(A, N, MPI_INT, 0, 0, MPI_COMM_WORLD, &st); for(i = 0; i < N; i ++) printf("%d ", A[i]); } MPI_Finalize(); }
132 ХАРАКТЕРНЫЕ ОШИБКИ В MPI-ПРОГРАММАХ
133 ВИДЫ ОШИБОК Ошибки последовательных программ. Ошибки несоответствия типов. Ошибки работы с MPI-объектами. Взаимные блокировки. Недетерминизм.
134 Недетерминизм за счет разницы в относительных скоростях процессов (race condition)
135 Deadlock if(rank == 0) { MPI_Ssend(… 1 …) MPI_Recv(…1…) } else { MPI_Ssend(… 0 …) MPI_Recv(…0…) }
136 «Недетерминированный» deadlock if(rank == 0) { MPI_Send(… 1 …) MPI_Recv(…1…) } else { MPI_Send(… 0 …) MPI_Recv(…0…) }
Еще похожие презентации в нашем архиве:
© 2024 MyShared Inc.
All rights reserved.