Обрамні функції
Існує кілька функцій, які використовуються в будь-якому, навіть найкоротшому додатку MPI. Займаються вони не так власне передачею даних, як її забезпеченням:
Ініціалізація бібліотеки. Одна з перших інструкцій у функції main (головної функції програми):
Аварійне закриття бібліотеки. Викликається, якщо програма користувача завершується через помилки часу виконання, пов'язані з MPI:
MPI_Abort (описувач області зв'язку, код помилки MPI);
Виклик MPI_Abort із будь-якої задачі примусово завершує роботу ВСІХ завдань, приєднаних до заданої області зв'язку. Якщо вказано описувач MPI_COMM_WORLD, буде завершено всю програму (всі її завдання) цілком, що, мабуть, і є найбільш правильним рішенням. Використовуйте код помилки MPI_ERR_OTHER, якщо не знаєте, як охарактеризувати помилку класифікації MPI.
Нормальне закриття бібліотеки:
Слід вписувати цю інструкцію перед поверненням із програми, тобто:
ü перед викликом стандартної функції Сі exit;
ü перед кожним після MPI_Init оператором return у функції main;
ü якщо функції main призначено тип void, і вона не закінчується оператором return, то MPI_Finalize() слід поставити в кінець main.
Дві інформаційні функції: повідомляють розмір групи (тобто загальна кількість завдань, приєднаних до її галузі зв'язку) та порядковий номер зухвалої задачі:
Приклад застосування цих функцій міститься у файлі “pi_mpi.c” – знаходження числа Пі. У ньому використовуються, окрім уже знайомих функцій, MPI_Bcast та MPI_Reduce. Ці функції розглядаються у параграфах: MPI_Bcast - Функції колективного обміну даними; MPI_Reduce – Функції підтримкирозподілених операцій).
2.4. Зв'язок "крапка-крапка"
Це найпростіший тип зв'язку між завданнями: одна гілка викликає функцію передачі, а інша - функцію прийому. У MPI це виглядає, наприклад, так:
Завдання 1 передає:
MPI_Send(buf, 5, MPI_INT, 1, 0, MPI_COMM_WORLD);
Завдання 2 приймає:
MPI_Recv( buf, 10, MPI_INT, 0, 0, MPI_COMM_WORLD, &status );
1.Адреса буфера, з якого в задачі 1 беруться, а в задачі 2 поміщаються дані. Пам'ятайте, що набори даних у кожного завдання свої, тому, наприклад, використовуючи одне й те саме ім'я масиву в кількох завданнях, Ви вказуєте не одну й ту саму область пам'яті, а різні, ніяк не пов'язані один з одним.
2. Розмір буфера. Задається над байтах, а кількості комірок. Для MPI_Send вказує, скільки осередків потрібно передати (у прикладі передаються 5 чисел). MPI_Recv означає максимальну ємність приймального буфера. Якщо фактична довжина повідомлення, що надійшло менше - останні осередки буфера залишаться недоторканими, якщо більше - відбудеться помилка часу виконання.
3. Тип осередку буфера. MPI_Send та MPI_Recv оперують масивами однотипних даних. Для опису базових типів Сі в MPI визначено константи MPI_INT, MPI_CHAR, MPI_DOUBLE тощо, мають тип MPI_Datatype. Їх назви утворюються префіксом " MPI_ " і ім'ям відповідного типу (int, char, double, . ), записаним великими літерами. Користувач може "реєструвати" в MPI свої типи даних, наприклад, структури, після чого MPI зможе обробляти їх нарівні з базовими.
4.Номер задачі, з якою відбувається обмін даними. Всі завдання всередині створеної групи MPI автоматично нумеруються від 0 до (розмір групи-1). У прикладі задача 0 передає задачі 1, задача 1 приймає від 0 задачі.
5. Ідентифікатор повідомлення. Це ціле число від 0 до 32 767, яке користувач вибирає сам. Воно служить тієї ж мети, що і, наприклад, розширення файлу - завдання-приймач:
ü за ідентифікатором визначає зміст прийнятої інформації;
ü повідомлення, які прийшли у невідомому порядку, може вилучати із загального вхідного потоку в потрібному алгоритмі порядку. Хорошим тоном є позначення ідентифікаторів символьними іменами за допомогою операторів #define або const int.
6.Описувач галузі зв'язку (комунікатор). Має бути однаковим для MPI_Send і MPI_Recv.
7.Статус завершення прийому. Містить інформацію про прийняте повідомлення: його ідентифікатор, номер завдання-передавача, код завершення і кількість даних, що фактично прийшли.
2.5. Прийом та передача: MPI_Sendrecv
Деякі конструкції з прийомом-передачею застосовуються дуже часто. Приклад – обмін даними із сусідами по групі (для парної кількості гілок у групі):
/* Гілки з парними номерами спочатку
* передають наступним непарним гілкам,
* потім приймають від попередніх
MPI_Send(. ( rank+1 ) % size . );
MPI_Recv(. ( rank+size-1 ) % size . );
/* Непарні гілки надходять навпаки:
* спочатку приймають від попередніх гілок,
* Потім передають наступним.
MPI_Recv(. ( rank-1 ) % size . );
MPI_Send(. ( rank+1 ) % size . );
Інший приклад – посилка даних та отримання підтвердження:
MPI_Send(. anyRank . ); /* Надсилаємо дані */
MPI_Recv(. anyRank . ); /* Приймаємо підтвердження */
Ситуація настільки поширена, що MPI спеціально введено дві функції, здійснюють одночасно посилку одних даних і прийом інших. Перша з них – MPI_Sendrecv. Вона має 12 параметрів:перші 5 параметрів такі ж, як у MPI_Send, решта 7 параметрів такі ж, як у MPI_Recv. Один її виклик робить ті ж дії, для яких у першому фрагменті потрібен блок IF-ELSE з чотирма викликами. Слід врахувати, що:
ü і прийом, і передача використовують один і той самий комунікатор;
ü порядок прийому та передачі даних MPI_Sendrecv вибирає автоматично; гарантується, що автоматичний вибір не призведе до "клінчу";
ü MPI_Sendrecv сумісна з MPI_Send і MPI_Recv, тобто може "спілкуватися" з ними.
MPI_Sendrecv_replace крім загального комунікатора використовує ще й загальний для приймання-передачі буфер. Не дуже зручно, що параметр count отримує подвійне тлумачення: це і кількість даних, що надсилаються, і гранична ємність вхідного буфера. Показання до застосування:
ü дані, що приймаються, повинні бути свідомо НЕ ДОВНІШЕ відправляються;
ü прийняті та надіслані дані повинні мати однаковий тип;
ü дані, що надсилаються, затираються прийнятими.
MPI_Sendrecv_replace також гарантовано не викликає клінчу.
Клінч (deadlock, глухий кут) – процес перебуває у стані глухого кута, якщо очікує події, яка ніколи не відбудеться. Приклад клінчу:
-- Гілка 1 -- -- Гілка 2 --
Recv (з гілки 2) Recv (з гілки 1)
Send (у гілку 2) Send (у гілку 1)
Це викличе клінч: функція прийому не поверне керування доти, доки не отримає дані; тому функція передачі не може розпочати відправлення даних; тому функція прийому. і так до самого SIG_KILL.
Колективні функції
Під терміном "колективні" в MPI маються на увазі три групи функцій:
ü функції колективного обміну даними;
ü точки синхронізації, або бар'єри;
ü функції підтримкирозподілених операцій.
Колективна функція однією з аргументів отримує описувач області зв'язку (комунікатор). Виклик колективної функції є коректним, тільки якщо зроблено з усіх процесів-абонентів відповідної галузі зв'язку, і саме з цим комунікатором як аргумент (хоча для однієї області зв'язку може бути кілька комунікаторів, підставляти їх замість один одного не можна). У цьому полягає колективність: або функція викликається всім колективом процесів, або ніким; третього не дано.
Для обмеження області дії колективної функції частиною гілок створюється тимчасова група/область зв'язку/комунікатор на базі існуючих, як це показано в розділі про комунікатори.