Главная страница
Навигация по странице:

  • Лекція 7. Операції обміну повідомленнями в MPI.

  • 1. Двоточковий обмін повідомленнями

  • 2. Блокуючі операції обміну

  • 2.1. Стандартний обмін

  • 2.2. Синхронний блокуючий обмін

  • трспо. Лекція Колективні операції обміну повідомленнями в mpi


    Скачать 3.37 Mb.
    НазваниеЛекція Колективні операції обміну повідомленнями в mpi
    Анкортрспо
    Дата23.11.2022
    Размер3.37 Mb.
    Формат файлаpdf
    Имя файлаilovepdf_merged.pdf
    ТипЛекція
    #806999
    страница2 из 12
    1   2   3   4   5   6   7   8   9   ...   12
    _Scatter збирає і розподіляє дані: int MPI_Reduce_Scatter(void *sendbuf, void *rcvbuf, int *rcvcounts,
    MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    Її вхідні параметри:
    • sendbuf — стартова адреса буфера прийому;
    • rcvcounts — цілочисельний одновимірний масив, який задає кількість елементів в результуючому масиві, що розподіляється кожному процесу. Цей масив має бути однаковим в усіх процесах, що викликають цю підпрограму;
    • datatype — тип даних у буфері прийому;
    • ор — операція;
    • comm — комунікатор.
    Вихідний параметр – стартова адреса буфера прийому rcvbuf.
    Особливістю цієї підпрограми є те, що кожне завдання отримує не увесь результуючий масив, а його частину.
    Підпрограма MPІ _ Allreduce збирає дані від усіх процесів і зберігає результат операції приведення в результуючому буфері кожного процесу:
    int MPI_Allreduce(void *sendbuf, void *rcvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    Її вхідні параметри:
    • sendbuf — початкова адреса буфера передачі;
    • count — кількість елементів у буфері передачі;
    • datatype — тип переданих даних;
    • ор — операція приведення;
    • comm — комунікатор.
    Вихідним параметром є стартова адреса буфера прийому rcvbuf. При аварійному завершенні підпрограма може повертати код помилки
    MPI_ERR_OP (некоректна операція). Це відбувається, якщо застосовується операція, яка не є зумовленою і яка не створена попереднім викликом підпрограми MPІ_op_create.
    Операції сканування (частковій редукції) виконуються за допомогою виклику підпрограми MPI_Scan: int MPI_Scan(void *sendbuf, void *rcvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
    Її вхідні параметри:
    • sendbuf — початкова адреса буфера передачі;
    • count — кількість елементів у вхідному буфері;
    • datatype — тип даних у вхідному буфері;
    • ор — операція;
    • comm — комунікатор.
    Вихідним параметром є стартова адреса буфера прийому rcvbuf.
    Рисунок 8.10. Загальна схема операції сканування
    У підпрограмі MPI_ можна використати тільки зумовлені типи MPI.
    Усі операції є асоціативними і комутативними (тобто операнди можуть по-різному групуватися, а їх порядок не має значення).
    Підпрограма MPI_Scanсхожа на підпрограму MPІ_Allreduce в тому відношенні, що кожен процес отримує результуючий масив. Відмінність
    полягає в тому, що вміст масиву-результату в процесі i є результатом виконання операції над масивами із завдань з номерами від 0 до i включно.
    У кожному процесі можна використати різні призначені для користувача операції. У MPI не визначено, які операції і над якими операндами застосовуватимуться в цьому випадку. У буферах передачі допускається перекриття типів, у буферах прийому це може привести до непередбачуваних результатів.
    Лістинг 8.3. Приклад використання операції редукції
    #include "mpi.h" finclude int main(int argc,char *argv[])
    { int rayrank, i; int count = 5, root = 1;
    MPI_Group MPI_GROUP_WORLD, subgroup; int ranks[4] = {1, 3, 5, 7};
    MPI_Comm subcomm; int sendbuf[5] = {1, 2, 3, 4, 5}; int recvbuf[5];
    MPI_Init(sargc, sargv);
    MPI_Comm_group(MPI_COMM_WORLD, &MPI_GROUP_WORLD);
    MPI_Group_incl(MPI_GROUP_WORLD, 4, ranks, Ssubgroup);
    MPI_Group_rank(subgroup, Smyrank);
    MPI_Coram_create(MPI_COMM_WORLD, subgroup, ssubcomm); if(myrank != MPI_UNDEFINED)
    {
    MPI_Reduce(&sendbuf, &recvbuf, count, MPI_INT, MPI_SUM, root, subcomm); if(myrank == root) { printf("Reduced values"); for(i = 0; i < count; i++){ printf(" %i ", recvbuf[i]);} printf ("\n") ;
    MPI_Comm_free (&subcomm) ;
    MPI_Group_free (&MPI_GROUP_WORLD)
    MPI_Group_free (&subgroup) ; }
    MPI_Finalize() ; return 0;
    }

    У цій програмі спочатку створюється підгрупа, що складається з процесів з рангами 1, 3, 5 і 7 (звідси витікає, що запускати її на виконання потрібно не менше чим у восьми процесах), і комунікатор, що відповідає їй.
    Редукція виконується тільки процесами з цієї групи. У кінці програми усі створені в процесі її роботи описувачі мають бути видалені.

    Лекція 7.
    Операції обміну повідомленнями в MPI.
    План лекції:
    1.
    Двоточковий обмін повідомленнями
    2.
    Блокуючі операції обміну
    2.1. Стандартний обмін
    2.2. Синхронний блокуючий обмін
    2.3. Буферизований обмін
    2.4. Обмін “по готовності”
    3.
    Підпрограми-пробники
    4.
    Спільні прийом і передача
    5.
    Не блокуючі операції обміну
    5.1. Ініціалізація не блокуючого обміну
    5.2. Перевірка виконання обміну
    6.
    Відстрочені обміни
    7.
    Скасування "чекаючих" обмінів.
    1. Двоточковий обмін повідомленнями
    Двоточковий обмін, з точки зору програміста, виконується наступним чином (рис. 7.1): для пересилання повідомлення процес-джерело викликає підпрограму передачі, при зверненні до якої зазначається ранг процесу- одержувача (адресата) у відповідній області взаємодії. Остання визначається своїм комунікатором, зазвичай це MPI_COMM_WORLD. Процес-одержувач, для того щоб отримати спрямоване йому повідомлення, повинен викликати підпрограму прийому, вказавши при цьому ранг джерела.
    Рисунок 7.1.
    Двоточковий обмін повідомленнями
    У всіх реалізаціях MPI, в тому числі і в MPICH, гарантується виконання деяких властивостей двоточкового обміну:
    Одним з них є збереження порядку повідомлень, які при двоточковому обміні не можуть "обганяти" один одного. Якщо, наприклад,
    процес з рангом 0 передає процесу з рангом 1 два повідомлення: А і В, процес 1 отримає спочатку повідомлення А, а потім В.
    Інша найважливіша властивість – гарантоване виконання обміну.
    Якщо один процес посилає повідомлення, а інший – запит на його прийом, то або передача або прийом будуть вважатися виконаними. При цьому можливі три сценарії обміну:
    • другий процес одержує від першого адресоване йому повідомлення;
    • надіслане повідомлення може бути отримано третім процесом, при цьому фактично виконана буде передача повідомлення, а не його прийом (повідомлення пройшло повз адресата);
    • другий процес одержує повідомлення від третього, тоді передача не може вважатися виконаною, тому що адресат отримав не той "лист".
    У двоточковому обміні слід дотримуватися правила відповідності типів переданих і прийнятих даних. Це ускладнює обмін повідомленнями між програмами, написаними на різних мовах програмування.
    У MPI є також чотири режими обміну, що розрізняються умовами
    ініціалізації і завершення передачі повідомлення:

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

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

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

    передача "по готовності" починається тільки в тому випадку, коли адресат ініціалізував прийом повідомлення, а завершується відразу, незалежно від того, прийнято повідомлення чи ні.
    Кожен з цих чотирьох режимів є як у блокуючій, так і в не блокуючій формі.
    У MPI прийняті наступні угоди про імена підпрограм доточкового обміну:
    MPI_[I][R, S, В]Send тут префікс [I] (Immediate) позначає не блокуючий режим. Один з префіксів [R, S, B] позначає режим обміну, відповідно: по готовності, синхронний і буферизований. Відсутність префікса позначає підпрограму
    стандартного обміну. Таким чином є вісім різновидів операції передачі повідомлень.
    Для підпрограм прийому:
    МРI_[I]Recv, тобто всього 2 різновиди прийому.
    Підпрограма MPI_Irsend, наприклад, виконує передачу "по готовності" в не блокуючому режимі, MPI_Bsend – буферизовану передачу з блокуванням, a MPI_Recv виконує блокуючий прийом повідомлень.
    Підпрограма прийому будь-якого типу може прийняти повідомлення від будь-якої підпрограми передачі.
    2. Блокуючі операції обміну
    Блокуючі операції призупиняють виконання вхідного процесу, змушуючи процес очікувати завершення передачі даних. Блокування гарантує виконання дій у заданому порядку, але і створює умови для виникнення тупикових ситуацій, коли обидва процеси-учасника обміну "точка-точка" блокуються одночасно.
    2.1.
    Стандартний обмін
    Повідомлення, відправлене в стандартному режимі, може протягом деякого часу "гуляти" по комунікаційній мережі паралельного комп'ютера, збільшуючи тим самим її завантаження. У MPI-програми рекомендується дотримувати наступних правил:
    • блокуючі операції обміну слід використовувати обережно, оскільки в цьому випадку зростає ймовірність тупикових ситуацій;
    • якщо для правильної роботи процесу має значення послідовність прийому повідомлень, джерело повинно передавати нове повідомлення, тільки переконавшись, що попереднє вже прийнято. В іншому випадку може порушитися детермінізм виконання програми;
    • повідомлення повинні гарантовано і з досить великою частотою прийматися процесами, яким вони надсилаються, інакше комунікаційна мережа може переповнитися, що призведе до різкого падіння швидкості її роботи. Це, в свою чергу, знизить продуктивність всієї системи.
    Основними підпрограма стандартного двоточкового обміну повідомленнями є MPI_Send і MPI_Recv. MPI_send – це підпрограма стандартної блокуючої передачі. Вона блокує виконання процесу до завершення передачі повідомлення (що, однак, не гарантує завершення прийому):
    Стандартна блокуюча передача виконується підпрограмою: int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
    Вхідні параметри підпрограми MPI_Send:
    • buf — адреса першого елемента у буфері передачі;
    • count — кількість елементів у буфері передачі;

    • datatype — тип MPI кожного елемента, що пересилається;
    • dest — ранг процесу-одержувача повідомлення. Ранг тут – ціле число від 0 до n-1, де n — число процесів в області взаємодії;
    • tag — тег повідомлення;
    • сомм — комунікатор;
    • ierr — код завершення.
    За замовчуванням реакцією системи на виникнення помилки під час виконання програми є її зупинка, однак реакцію можна змінити за допомогою виклику підпрограми MPI_Errhandler_set. Слід мати на увазі, що
    MPI не гарантує нормального продовження роботи програми після виникнення помилки.
    При виклику підпрограм обміну можуть бути такі помилки:

    MPI_ERR_COMM — неправильно вказано комунікатор.
    Часто виникає при використанні "порожнього" комунікатора;

    MPI_ERR_COUNT — неправильне значення аргументу count
    (кількість значень, що пересилаються);

    MPI_ERR_TYPE — неправильне значення аргументу, що задає тип даних;

    MPI_ERR_TAG — неправильно вказано тег повідомлення;

    MPI_ERR_RANK — неправильно вказано ранг джерела або одержувача повідомлення;

    MPI_ERR_ARG — неправильний аргумент, помилкове завдання якого не потрапляє ні в один клас помилок;

    MPI_ERR_REQUEST — неправильний запит на виконання операції.
    Стандартний блокуючий прийом виконується підпрограмою: int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
    Її вхідні (для комунікатора) параметри:
    • count -- максимальна кількість елементів у буфері прийому.
    Фактичну їх кількість можна визначити за допомогою підпрограми
    MPI_Get_count;
    • datatype — тип прийнятих даних. Обов'язкове дотримання відповідності типів аргументів підпрограм прийому і передачі;
    • source — ранг джерела. Можна використовувати спеціальне значення MPI_ANY_SOURCE, що відповідає довільному значенню рангу. У програмуванні ідентифікатор, що відповідає довільному значенню параметра, часто називають "джокером";
    • tag — тег повідомлення або "джокер" MPI_ANY_TAG, що відповідає довільному значенню тегу;
    • comm — комунікатор. При вказівці комунікатора "джокери" використовувати не можна.

    Слід мати на увазі, що при використанні значень MPI_ANY_SOURCE
    (будь-яке джерело) і MPI_ANY_TAG (будь-який тег) є небезпека прийому повідомлення, не призначеного даному процесу.
    Вихідними (для комунікатора) параметрами є:
    • buf — початкова адреса буфера прийому. Його розмір повинен бути достатнім, щоб розмістити прийняте повідомлення, інакше при виконанні прийому відбудеться збій – виникне помилка переповнення;
    • status – статус обміну.
    Якщо повідомлення менше, ніж буфер прийому, змінюється вміст лише тих осередків пам'яті буфера, які відносяться до повідомлення.
    Інформація про довжину отриманого повідомлення міститься в одному з полів статусу, але до цієї інформації у програміста немає прямого доступу (як до поля структури або елементу масиву).
    Розмір отриманого повідомлення (count) можна визначити з допомогою виклику підпрограми MPI_Get_count: int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int
    *count)
    Аргумент datatype повинен відповідати типу даних, зазначеному в операції обміну.
    В структурі MPI_Status прописані атрибуты принятого повідомлення.
    Вона має вигляд:
    typedef struct { int count;//количество байт в принятом сообщении
    int MPI_SOURCE;//идентификатор процесса отправителя
    int MPI_TAG;//идентификатор сообщения
    int MPI_ERROR;//код ошибки } MPI_Status;
    Нехай в програмі описана структура MPI_Status, яку назвемо status. Для звернення до полів структури будемо використовувати:
    1) для визначення ідентификатора процеса відправника status.MPI_SOURCE
    2) для визначення тега повідомлення status.MPI_TAG
    3) для визначення кода помилки status.MPI_ERROR
    4) поле count є private, тому для визначення кількості елементів в прийнятому повідомлення використовують функцію: int MPI_Get_count(MPI_Status *status, MPI_Datatype type, int *count)
    MPI_Status *status – адрес структури типу MPI_Status
    MPI_Datatype type – тип елементів в прийнятому повідомленні в термінології MPI int *count–- адреса змінної цілого типу, куди буде записано кількість елементів в прийнятому повідомленні.
    Підпрограма MPI_Recv може приймати повідомлення, відправлені в будь-якому режимі. Є деяка асиметрія між операціями прийому та передачі.

    Вона полягає в тому, що прийом може виконуватися від довільного процесу, а в операції передачі повинна бути вказана цілком певна адреса. Приймач може використовувати "джокери" для джерела і для тегу. Процес може відправити повідомлення і самому собі, але слід враховувати, що в цьому випадку блокуючі операції можуть призвести до "глухого кута".
    Перед завершенням програми та виконанням MPI_Finalize слід переконатися, що прийняті всі повідомлення, надіслані раніше.
    Лістинг 7.1. Приклад 1 використання стандартного блокуючого обміну
    #include "mpi.h"
    #include int main(int argc, char *argv[])
    { int myid, numprocs; char message[24] ; int myrank; MPI_Status status; int TAG = 0;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank (MPI_COMM_WORLD, &myrank) ; if (myrank == 0) { strcpy(message, "Hi, Parallel Programmer!");
    MPI_Send(&message, 25, MPI_CHAR, 1, TAG, MPI_COMM_WORLD); } else {
    MPI_Recv(&message, 25, MPI_CHAR, 0,
    TAG, MPI_COMM_WORLD, &status); printf("received: %s\n", message); }
    MPI_Finalize(); return 0;
    }
    У даному прикладі процес з рангом 0 передає повідомлення процесу з рангом 1, використовуючи для цього підпрограму MPI_Send. При виконанні цієї операції в пам'яті процесу-відправника виділяється буфер передачі, який містить змінну message. У буфері містяться 24 символи. Процес з рангом 1 приймає повідомлення з допомогою підпрограми MPI_Recv. Повідомлення зберігається в буфері прийому. Перші три аргументи підпрограми прийому визначають положення, розмір і тип буфера прийому.
    Результат виконання цієї програми:
    received: Hi, Parallel Programmer!
    Приклад програми, в якому обмінюються повідомленнями процеси з парними і непарними рангами, дано в лістингу 7.2. Передбачається, що значення size парно.
    Лістинг 7.2. Приклад 2 використання стандартного блокуючого обміну
    #include "mpi.h"
    #include int main(int argc, char *argv[])
    { int myrank, size, message; int TAG = 0;
    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
    MPI_Comm_size(MPI_COMM_WORLD, &size); message = myrank; if((myrank % 2) == 0)
    { if((myrank + 1) != size) MPI_Send(&message, 1, MPI_INT, myrank + 1,
    TAG, MPI_COMM_WORLD);
    } else
    { if(myrank != 0)
    MPI_Recv(&message,
    -1,
    MPI_INT, myrank-1,
    TAG,
    MPI_COMM_WORLD, &status); printf("received :%i\n", message);
    }
    MPI_Finalize(); return 0;
    }
    Результат виконання цієї програми у разі запуску процесів 10 виглядає так: received :0 received :2 received :6 received :4 received :8
    Порядок виведення повідомлень може бути іншим, він змінюється від випадку до випадку.
    Якщо стандартна передача не може бути виконана з-за недостатнього обсягу буфера прийому, здійснення процесу блокується до тих пір, поки не буде доступний буфер достатнього розміру. Іноді це може виявитися зручним. Наведемо приклад. Нехай джерело циклічно посилає нові значення адресату і нехай вони виробляються швидше, ніж адресат може їх прийняти.
    При використанні буферизованої передачі може виникнути переповнення буфера прийому. Для того щоб його уникнути, в програму доведеться
    включити додаткову синхронізацію, а при використанні стандартної передачі така синхронізація виконується автоматично. Іноді недостатній розмір буфера може призвести до тупикової ситуації.
    2.2.
    Синхронний блокуючий обмін
    При синхронному обміні адресат посилає джерелу "квитанцію" – повідомлення про завершення прийому. Тільки після отримання цього повідомлення обмін вважається завершеним і джерело "знає", що його повідомлення отримано.
    Синхронна передача виконується з допомогою підпрограми
    MPІ_Ssend: int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
    Її параметри збігаються з параметрами підпрограми MРІ_Send.
    Якщо процес виконує блокуючу синхронну передачу до того, як
    інший процес спробує отримати повідомлення, він зупиняється до прийому повідомлення адресатом. Синхронний обмін повільніше, ніж стандартний, але він безпечніше, оскільки не дає комунікаційної мережі переповнитися "втраченими в мережі" повідомленнями, які не дійшли до адресата. Цим забезпечується більший ступінь передбачуваності поведінки паралельної програми. Завдяки відсутності "невидимих" повідомлень, простіше виявляється і налагодження програми.
    Передачу повідомлення в синхронному режимі може бути розпочато незалежно від того був зареєстрований його прийом чи ні, але вона вважається успішно виконаною тільки при наявності відповідного прийому.
    Завершення синхронної передачі означає не тільки те, що буфер передачі можна використовувати заново, але і те, що адресат розпочав виконання прийому. Якщо і передача і прийом є блокуючими операціями, обмін не завершиться, поки обидва процеси не почнуть передачу і прийом повідомлень. Операція передачі в цьому режимі не локальна.
    1   2   3   4   5   6   7   8   9   ...   12


    написать администратору сайта