p align="left">rcvtype - тип прийнятих даних; root - ранг передавального процесу; comm - комунікатор. Вихідний параметр rcvbuf - адреса буфера прийому. Працює ця підпрограма в такий спосіб. Процес з рангом root ("головний процес") розподіляє вміст буфера передачі sendbuf серед усіх процесів. Уміст буфера передачі розбивається на кілька фрагментів, кожний з який містить sendcount елементів. Перший фрагмент передається процесу 0, другий процесу 1 і т.д. Аргументи send мають значення тільки на стороні процесу root. При зборці (MPI_Gather) кожен процес у комунікаторі comm пересилає вміст буфера передачі sendbuf процесу з рангом root. Процес root "склеює" отримані дані в буфері прийому. Порядок склейки визначається рангами процесів, тобто в результуючому наборі після даних від процесу 0 випливають дані від процесу 1, потім дані від процесу 2 і т.д. Аргументи rcvbuf, rcvcount і rcvtype відіграють роль тільки на стороні головного процесу. Аргумент rcvcount указує кількість елементів даних, отриманих від кожного процесу (але не їхня сумарна кількість). При виклику підпрограм MPI_scatter і MPI_Gather з різних процесів варто використовувати загальний головний процес. Операції приведення і сканування Операції приведення і сканування відносяться до категорії глобальних обчислень. У глобальній операції приведення до даних від усіх процесів із заданого комунікатора застосовується операція MPI_Reduce (див рис). Аргументом операції приведення є масив даних -- по одному елементі від кожного процесу. Результат такої операції -- єдине значення (тому вона і називається операцією приведення). У підпрограмах глобальних обчислень функція, передана в підпрограму, може бути: визначеною функцією MPI, наприклад MPI_SUM, користувальницькою функцією, а також оброблювачем для користувальницької функції, що створюється підпрограмою MPI_Op_create. Три версії операції приведення повертають результат: одному процесу; усім процесам; розподіляють вектор результатів між усіма процесами. Операція приведення, результат якої передається одному процесу, виконується при виклику підпрограми MPI_Reduce: int MPI_Reduce(void *buf, void *result, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) Вхідні параметри підпрограми MPI_Reduce: buf -- адреса буфера передачі; count -- кількість елементів у буфері передачі; datatype -- тип даних у буфері передачі; ор -- операція приведення; root -- ранг головного процесу; comm -- комунікатор. Підпрограма MPI_Reduce застосовує операцію приведення до операндам з buf, а результат кожної операції міститься в буфер результату result. MPI_Reduce повинна викликатися всіма процесами в комунікаторі comm, a аргументи count, datatype і op у цих викликах повинні збігатися. Функція приведення (ор) не повертає код помилки, тому при виникненні аварійної ситуації або завершується робота всієї програми, або помилка мовчазно ігнорується. І те й інше в однаковій мірі небажано. У MPI мається 12 визначених операцій приведення (див. табл.). |
Операція | Опис | | MPI_MAX | Визначення максимальних значень елементів одномірних масивів цілого чи речовинного типу | | MPI_MIN | Визначення мінімальних значень елементів одномірних масивів цілого чи речовинного типу | | MPI_SUM | Обчислення суми елементів одномірних масивів цілого, речовинного чи комплексного типу | | MPI_PROD | Обчислення заелементного добутку одномірних масивів цілого, речовинного чи комплексного типу | | MPI_LAND | Логічне "И" | | MPI_BAND | Бітове "И" | | MPI_LOR | Логічне "ЧИ" | | MPI_BOR | Бітове "ЧИ" | | MPI_LXOR | Логічне "ЧИ", що виключає | | MPI_BXOR | Бітове "ЧИ", що виключає | | MPI_MAXLOC | Максимальні значення елементів одномірних масивів і їхні індекси | | MPI_MINLOC | Мінімальні значення елементів одномірних масивів і їхні індекси | | |
Розглянемо приклад 3: ===== Example2.cpp ===== #include <mpi.h> #include <stdio.h> #include <math.h> int main(int argc, char *argv[]) { int n, myid, numprocs, i; double PI25DT = 3.141592653589793238462643; double mypi, pi, h, sum, x; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); 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; else { 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; 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(); return 0; } ===== Example2.cpp ===== Ця програма обчислює число р методом підсумовування ряду. Спочатку один із процесів (0) запитує число інтервалів, що потім поширює іншим процедурою MPI_Bcast. Помітьте, що процедура MPI_Bcast для процесу 0 є передавальної, а для всіх інших - приймаючої. Кінцеві результати обчислень здаються процесу 0 для підсумовування: процедура MPI_Reduce(&mypi,&pi,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD) збирає з усіх процесів перемінну mypi, підсумовує (MPI_SUM), і зберігає результат у змінної pi процесу 0. Вивід приклада: Example3 output (np = 6) Process 5 on apc-pc. Process 3 on apc-pc. Process 0 on apc-pc. Enter the number of intervals: (0 quits) Process 1 on apc-pc. Process 2 on apc-pc. Process 4 on apc-pc. 15 pi is approximately 3.1419630237914191, Error is 0.0003703702016260 wall clock time = 0.031237 Enter the number of intervals: (0 quits) 2 pi is approximately 3.1623529411764704, Error is 0.0207602875866773 wall clock time = 0.000943 Enter the number of intervals: (0 quits) 0 Завдання 1: поясніть вивід;) Приклад 4 показує створення комплексної системи керування процесами на прикладі розподіленого дешифратора паролів. Використовується структура master-slave (головн-підлеглий). ===== Example4.cpp ===== #include <mpi.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define TAG_READY 99 #define TAG_RESULT 98 int do_decrypt_pass(char* incoming_pass_str, char * result_pass_str, int length) { if (length % 2 == 0) return 1; else return 0; } int main(int argc, char* argv[]) { int k,x; char in_line[256],acc_name[256],acc_pass[256]; int myrank, size; MPI_Status status; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&size); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if (myrank == 0) // kind'a Master Process { puts("Initializing"); fflush(stdout); FILE* in_file = fopen("pass.txt","r"); for (x=1;x<size;x++) MPI_Recv (&k, 1, MPI_INT, x, TAG_READY, MPI_COMM_WORLD, &status); char* p; puts ("Feeding"); fflush(stdout); sprintf(in_line,"apc::1234"); if (p = strtok(in_line,"::")) sprintf(acc_name,"%s",p); else return 0; if (p = strtok(NULL,"::")) sprintf(acc_pass,"%s",p); else return 0; int acc_name_len = strlen(acc_name)+1, acc_pass_len = strlen(acc_pass)+1; MPI_Bcast(&acc_name_len, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&acc_pass_len, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&acc_name, acc_name_len, MPI_CHAR, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&acc_pass, acc_pass_len, MPI_CHAR, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); for (x=1;x<size;x++) { MPI_Probe(MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status); int src = status.MPI_SOURCE; int res; MPI_Recv(&res, 1, MPI_INT, src, TAG_RESULT, MPI_COMM_WORLD, &status); printf("Proc %d returned %d\n",src,res);fflush(stdout); } } else { MPI_Ssend(&myrank, 1, MPI_INT, 0, TAG_READY, MPI_COMM_WORLD); int acc_name_len, acc_pass_len; MPI_Bcast(&acc_name_len, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&acc_pass_len, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&acc_name, acc_name_len, MPI_CHAR, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&acc_pass, acc_pass_len, MPI_CHAR, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); printf("Proc %d: recv %s:: %s\n",myrank,acc_name,acc_pass);fflush(stdout); char[256] ret_pass; int result = do_decrypt_pass(&acc_pass, &ret_pass, myrank); MPI_Ssend(&result, 1, MPI_INT, 0, TAG_RESULT, MPI_COMM_WORLD); } MPI_Finalize(); return 0; } ===== Example4.cpp ===== У цьому прикладі головний процес (ранг 0) чекає підключення всіх підлеглих процесів (посилки ними повідомлення з тегом TAG_READY), розсилає рядок in_line усім підлеглим процесам, що намагаються підібрати пароль довжини myrank (тобто власний номер процесу). Власне зломом займається функція int do_decrypt_pass(char* incoming_pass_str, char * result_pass_str, int length) Процеси повертають результат підбора c повідомленням TAG_RESULT. MPI_Barrier використовується для синхронізації. Висновок приклада: Example4 output (np = 5) Initializing Feeding Proc 1: recv apc:: 1234 Proc 2: recv apc:: 1234 Proc 3: recv apc:: 1234 Proc 1 returned 0 Proc 4: recv apc:: 1234 Proc 3 returned 0 Proc 2 returned 1 Proc 4 returned 1 У цьому прикладі всі процеси ламають той самий пароль, і новий цикл (не реалізований у прикладі) не почнеться, поки не завершать роботу всі процеси. Отже, час одного циклу визначається часом роботи процесу з максимальним рангом (тобто виконуючого підбор найбільшої довжини => перебір найбільшого числа комбінацій). Приклад 5 показує більш зроблену систему, що читає з необхідну інформацію з файлу, і роздає кожному процесу по паролі. Процеси працюють в асинхронному режимі, зв'язуючи з головним процесом, що відіграє роль «роздавального-прийомного центра», організовуючи систему дуже схожу на «клієнта-сервер». Завдання 2: Після вивчення коду поясніть, чому це не є системою клієнт-сервер. ===== Example5.cpp ===== #include <mpi.h>
Страницы: 1, 2, 3, 4
|