Смекни!
smekni.com

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

стандартна передача вважається виконаною і завершується, як тільки повідомлення відправлене, незалежно від того, дійшло воно до чи адресата ні. У стандартному режимі передача повідомлення може починатися, навіть якщо ще не початий його прийом;

синхронна передача відрізняється від стандартної тим, що вона не завершується доти, поки не буде довершений прийом повідомлення. Адресат, одержавши повідомлення, посилає процесу, що відправив його, повідомлення, що повинне бути отримане відправником для того, щоб обмін вважався виконаним. Операцію передачі повідомлення іноді називають "рукостисканням";

буферизована передача завершується відразу ж, повідомлення копіюється в системний буфер, де й очікує своєї черги на пересилання. Завершується буферизованна передача незалежно від того, виконаний прийом чи повідомлення ні;

передача "по готовності" починається тільки в тому випадку, коли адресат ініціалізував прийом повідомлення, а завершується відразу, незалежно від того, прийняте чи повідомлення ні.

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

У MPI прийняті наступні угоди про імена підпрограм двохточкового обміну: MPI_[I][R|S|B]Send. Префікс I (Immediate) позначає режим, неблокуючий, один із префіксів R|S|B позначає режим обміну по відповідно готовності, синхронний і буферизований, відсутність префікса позначає стандартний обмін. Разом – 8 різновидів передачі повідомлень. Для прийому ж існує всього 2 різновиди: MPI_[I]Recv. Приклади: MPI_Irsend - виконує передачу «по готовності» у режимі, неблокуючий, MPI_Bsend - буферизована передача з блокуванням, MPI_Recv - прийом, що блокуючий. Відзначимо, що підпрограма прийому будь-якого типу може прийняти повідомлення від будь-якої програми передачі. Перейдемо до приклада 2:

===== Example2.cpp =====

#include <mpi.h>

#include <stdio.h>

#define TAG_SEND_FWD 99

#define TAG_SEND_BACK 98

#define TAG_REPLY 97

int main(int argc, char* argv[])

{

int k,x;

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) // призначимо один процес головним

{

puts("Running procs forwards"); fflush(stdout); // негайний вивід повідомлення

x=1;

while (x < size)

{

MPI_Ssend(&x, 1, MPI_INT, x, TAG_SEND_FWD, MPI_COMM_WORLD);

MPI_Recv (&k, 1, MPI_INT, x, TAG_REPLY, MPI_COMM_WORLD, &status);

printf("Reply from proc %d received %d&bsol;n",x,k); fflush(stdout);

x++;

}

puts("Running procs backwards"); fflush(stdout);

x=size-1;

while (x >0)

{

MPI_Send(&x,1, MPI_INT, x, TAG_SEND_BACK, MPI_COMM_WORLD);

x--;

}

else // інші процеси - підлеглі

{

MPI_Recv(&k, 1, MPI_INT, 0, TAG_SEND_FWD, MPI_COMM_WORLD, &status);

printf("Proc %d received %d&bsol;n",myrank,k); fflush(stdout);

MPI_Ssend(&k,1, MPI_INT, 0, TAG_REPLY, MPI_COMM_WORLD);

MPI_Recv(&k, 1, MPI_INT, 0, TAG_SEND_BACK, MPI_COMM_WORLD, &status);

printf("Proc %d Received %d&bsol;n",myrank,k); fflush(stdout);

}

MPI_Finalize();

return 0;

}

===== Example2.cpp =====


У цьому прикладі один із процесів (з рангом 0) розсилає повідомлення іншому у прямому, а потім у зворотному порядку.

Прототип функції: int MPI_[..]Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm).

Вхідні параметри (однакові для усіх функцій *send):

buf – адреса першого елемента в буфері передачі

count – кількість елементів у буфері передачі

Тип даних MPI Тип даних З
MPI_CHAR signed char
MPI_SHORT signed short int
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
MPI_BYTE Ні відповідності
MPI_PACKED Ні відповідності

datatype – тип MPI кожного переданого елемента. MPI визначає власні типи даних, схожі на типи даних C, однак, існують і унікальні для MPI типи (див. таблицю відповідності). У MPI повинні дотримуватися правила сумісності типів, з базових типів можуть бути сконструйовані більш складні.

dest – ранг процесу-одержувача повідомлення. Ранг тут – ціле число від 1 до n-1, де n – число процесів в області взаємодії

tag – тег – унікальний ідентифікатор повідомлення

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

Стандартна передача, що блокуючий, починається незалежно від того, чи був зареєстрований відповідний прийом, а завершується тільки після того, як повідомлення прийняте системою і процес-джерело може знову використовувати буфер передачі. Повідомлення може бути скопійоване прямо в буфер прийому, а може бути поміщене в тимчасовий системний буфер, де і буде чекати виклику адресатом підпрограми прийому. У цьому випадку говорять про буферизації повідомлення. Передача може завершитися ще до виклику відповідної операції прийому. З іншого боку, буфер може бути недоступний чи MPI може вирішити не буферизувати вихідні повідомлення з міркувань збереження високої продуктивності. У цьому випадку передача завершиться тільки після того, як буде зареєстровані відповідний прийом і дані будуть передані адресату.

Прийом виконується підпрограмою:

int MPI_Recv (void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status * status)

Її вхідні параметри (у MPI_Irecv – такі ж):

count – максимальна кількість елементів у буфері прийому. Фактична їхня кількість можна визначити за допомогою підпрограми MPI_Get_count;

datatype – тип прийнятих даних. Нагадаємо про необхідність дотримання відповідності типів аргументів підпрограм прийому і передачі;

source – ранг джерела. Можна використовувати спеціальне значення mpi_any_source, що відповідає довільному значенню рангу. У програмуванні ідентифікатор, що відповідає довільному значенню параметра, часто називають "джокером". Цей термін будемо використовувати і ми;

tag – тег чи повідомлення "джокер" mpi_any_tag, що відповідає довільному значенню тега;

comm — комунікатор. При вказівці комунікатора "джокери" використовувати не можна.

Варто мати на увазі, що при використанні значень mpi_any_source (будь-яке джерело) і mpi_any_tag (будь-який тег) є небезпеку прийому повідомлення, не призначеного даному процесу.

Вихідними параметрами є:

buf – початкова адреса буфера прийому. Його розмір повинний бути достатнім, щоб розмістити прийняте повідомлення, інакше при виконанні прийому відбудеться збій – виникне помилка переповнення;

status – статус обміну – спеціальна структура MPI.

Якщо повідомлення менше, ніж буфер прийому, змінюється вміст лише тих комірок пам'яті буфера, що відносяться до повідомлення. Інформація про довжину прийнятого повідомлення міститься в одному з полів статусу, але до цієї інформації в програміста немає прямого доступу (як до поля рядка чи елементу масиву). Розмір отриманого повідомлення (count) можна визначити за допомогою виклику підпрограми MPI_Get_count:

int MPI_Get_count (MPI_Status *status, MPI_Datatype datatype, int *count)

Аргумент datatype повинний відповідати типу даних, зазначеному в операції обміну.

Висновок Example2 output(np = 6): Ssend & replies

Running procs forwards

Proc 1 received 1

Reply from proc 1 received 1

Proc 2 received 2

Reply from proc 2 received 2

Proc 3 received 3

Reply from proc 3 received 3

Proc 4 received 4

Reply from proc 4 received 4

Proc 5 received 5

Reply from proc 5 received 5

Running procs backwards

Proc 5 Received 5

Proc 4 Received 4

Proc 3 Received 3

Proc 2 Received 2

Proc 1 Received 1

Завдання 1: Проаналізуйте висновок приклада. Спробуйте забрати зворотні повідомлення (replies), що зміниться? Застосуєте різні сполучення *send (Send,Ssend,Rsend) і прокоментуйте відповідні висновки програм.

Завдання 2: Зміните топологію пересилання повідомлень у Прикладі 2 з «зірки» на «кільце» (див. мал.). Проробіть усі те ж, що й у Завданні 1.

3.3 Колективний обмін даними.

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

У будь-якому колективному обміні бере участь кожен процес з деякої області взаємодії. Можна організувати обмін і в підмножині процесів, для цього маються засоби створення нових областей взаємодії і відповідних їм комунікаторів. Колективні обміни характеризуються наступним:

колективні обміни не можуть взаємодіяти з двохточковими. Колективна передача, наприклад, не може бути перехоплена двохточковой підпрограмою прийому;

колективні обміни можуть виконуватися як із синхронізацією, так і без її;

усі колективні обміни є блокирующими для їхній обменаЭЪ, що ініціював;

теги повідомлень призначаються системою.

Широкомовне розсилання

Широкомовне розсилання виконується одним виділеним процесом, що називається головним (root), а всі інші процеси, що приймають участь в обміні, одержують по одній копії повідомлення від головного процесу:

Виконується широкомовне розсилання за допомогою підпрограми

int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)

Її параметри одночасно є вхідними і вихідними:

buffer — адреса буфера;

count — кількість елементів даних у повідомленні;

datatype — тип даних MPI;

root — ранг головного процесу, що виконує широкомовне розсилання;