Смекни!
smekni.com

Паралельні обчислення з використанням MPI (стр. 3 из 5)

comm — комунікатор.

Обмін із синхронізацією

Синхронізація за допомогою "бар'єра" є найпростішою формою синхронізації колективних обмінів. Вона не вимагає пересилання даних. Підпрограма MPI_Barrier блокуючий виконання кожного процесу з комунікатора comm доти, поки всі процеси не викликають цю підпрограму: int MPI_Barrier(MPI_Comm comm)

Розподіл і збір даних

Розподіл і збір даних виконуються за допомогою підпрограм MPI_Scatter і MPI_Gather відповідно. Список аргументів в обох підпрограм однаковий, але діють вони по-різному.

Схеми передачі даних для операцій збору і розподілу даних приведені на малюнках.

Повний список підпрограм розподілу і збору даний приведений у таблиці:

Підпрограма Короткий опис
MPI_Allgather Збирає дані від усіх процесів і пересилає їх усім процесам
MPI_Allgatherv Збирає дані від усіх процесів і пересилає їх усім процесам ("векторний" варіант підпрограмиMPI_Allgather)
MPI_Allreduce Збирає дані від усіх процесів, виконує операцію приведення, і результат розподіляє всім процесам
MPI_Alltoall Пересилає дані від усіх процесів усім процесам
MPI_Alltoallv Пересилає дані від усіх процесів усім процесам ("векторний" варіант підпрограми MPI_Alltoall)
MPI_Gather Збирає дані від групи процесів
MPI_Gatherv Збирає дані від групи процесів ("векторний" варіант підпрограми MPI Gather)
MPI_Reduce Виконує операцію приведення, тобто обчислення єдинного значення по масиву вихідних даних
MPI_Reduce_scatter Збір значень з наступним розподілом результата операції приведення
MPI_Scan Виконання операції сканування (часткова редукція) для даних від групи процесів
MPI_Scatter Розподіляє дані від одного процесу всім іншим процесам у групі
MPI_Scatterv Пересилає буфер вроздріб усім процесам у групі ("векторний" варіант підпрограми MPI_Scatter)

При широкомовному розсиланні всім процесам передається той самий набір даних, а при розподілі передаються його частини. Виконує розподіл даних підпрограмою MPI_Scatter, що пересилає дані від одного процесу всім іншим процесам у групі так, як це показано на малюнку.

int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *rcvbuf, int rcvcount, MPI_Datatype rcvtype, int root, MPI_Comm comm)

Її вхідні параметри (параметри підрограми MPI_Gather такі ж):

sendbuf – адреса буфера передачі;

sendcount – кількість елементів, що пересилаються кожному процесу (але не сумарна кількість елементів, що пересилаються,);

sendtype – тип переданих даних;

rcvcount – кількість елементів у буфері прийому;

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&bsol;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&bsol;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);