трспо. Лекція Колективні операції обміну повідомленнями в mpi
Скачать 3.37 Mb.
|
Лекція 8. Колективні операції обміну повідомленнями в MPI. План лекції: 1. Широкомовна розсилка 2. Обмін з синхронізацією 3. Розподіл і збір даних 4. Векторні підпрограми розподілу даних 5. Операції передачі даних від всіх процесів всім процесам 6. Операції приведення і сканування У будь-якому колективному обміні бере участь кожен процес з деякої області взаємодії (рис. 8.1). Можна організувати обмін і в підмножині процесів, для цього є засоби створення нових областей взаємодії і відповідних їм комунікаторів. У MPI є підпрограми, що виконують операції розподілу і збору даних, глобальні математичні операції, такі як додавання елементів масиву або його обчислення максимального елемента і т.п. Рисунок 8.1. Колективний обмін даними Колективні обміни характеризуються наступними властивостями: • колективні обміни не можуть взаємодіяти з двоточковими. Колективна передача, наприклад, не може бути перехоплена двоточковою підпрограмою прийому; • колективні обміни можуть виконуватися як із синхронізацією, так і без неї; • всі колективні обміни є блокуючими для обміну, який ініціював їх; • теги повідомлень призначаються системою. 1. Широкомовна розсилка Широкомовна розсилка виконується одним виділеним процесом, який називається головним (root), а всі інші процеси, які беруть участь в обміні, отримують по одній копії повідомлення від головного процесу Рисунок 8.2. Широкомовна розсилка Виконується широкомовна розсилка з допомогою підпрограми MPI_Bcast: int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm) Її параметри одночасно є вхідними та вихідними: • buffer — адреса буфера; • count — кількість елементів даних у повідомленні; • datatype — тип данихMPI; • root — ранг головного процесу, що виконує широкомовну розсилку; • comm — комунікатор. Особливості: 1. Функція MPI_Bcast визначає колективну операцію і, тим самим, при виконанні необхідних розсилок даних виклик функції MPI_Bcast має бути здійснений всіма процесами комунікатора що вказано. 2. Вказаний у функції Mpi_Bcast буфер пам'яті має різне призначення в різних процесах. Для процесу з рангом root, з якого здійснюється розсилка даних, в цьому буфері повинне знаходитися повідомлення, що розсилається. Для всіх останніх процесів вказаний буфер призначений для прийому даних. 2. Обмін з синхронізацією Синхронізація за допомогою "бар'єру"є найпростішою формою синхронізації колективних обмінів. Вона не вимагає пересилання даних. Підпрограма MPІ_Barrierблокує виконання кожного процесу з комунікатора comm до тих пір, поки всі процеси не викличуть цю підпрограму. Параметром підпрограми є поточний комунікатор: int MPI_Barrier(MPI_Comm comm.) Рисунок 8.3. Загальна схема операції сихронізації Лістинг 8.1. Приклад використання колективного обміну: #include "mpi.h" #include { char data[24]; int myrank, count = 25; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if (myrank == 0) { strcpy(data, "Hi, Parallel Programmer!"); MPI_Bcast (&data, count, MPI_BYTE, 0, MPI_ COMM _WORLD) ; printf("send: %s\n", data); } else{ MPI_Bcast(&data, count, MPI_BYTE, 0, MPI_COMM_WORLD); printf("received: %s\n", data); } MPI_Finalize(); return 0; } Результат виконання цієї програми: send:Hi, Parallel Programmer! received: Hi, Parallel Programmer! received: Hi, Parallel Programmer! Лістинг 8.2. Приклад використання широкомовної розсилки #include "mpi.h" #include { int my rank; int root = 0; int count = 1; float a, b; int n; MPI_Init (&argc, &argv) ; MPI_Comm_rank(MPI_COMM_WORLD, &myrank) ; if (myrank == 0) { printf ("Enter a, b, n\n"); scanf("%f %f %i", &a, &b, &n) ; MPI_Bcast (&a, count, MPI_FLOAT, root, MPI_COMM_WORLD) MPI_Bcast (&b, count, MPI_FLOAT, root, MPI_COMM_WORLD) MPI_Bcast(&n, count, MPI_INT, root, MPI_COMM_WORLD) ; } else { MPI_Bcast(&a, count, MPI_FLOAT, root, MPI_COMM_WORLD) ; MPI Bсast (&b, count, MPI FLOAT, root, MPI COMM_WORLD) ; MPI_Bcast (&n, count, MPI_INT, root, MPI_COMM_WORLD) ; printf("%i Process got %f %f %i\n", myrank, a, b, n) } MPI_Finalize() ; return 0; } 3. Розподіл і збір даних Розподіл і збір даних виконуються з допомогою підпрограм MPI_Scatter і MPI_Gather відповідно. Список аргументів у обох підпрограм однаковий, але діють вони по-різному. Рисунок 8.4. Схема передачі даних для операції розподілу даних Рисунок 8.5. Схема передачі даних для операції збору даних При широкомовній розсилці всім процесам передається один і той же набір даних, а при розподілі передаються його частини. Виконується розподіл даних підпрограмою MPI_Scatter, яка пересилає дані від одного процесу всім іншим процесам в групі. int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *rcvbuf, int rcvcount, MPI_Datatype rcvtype, int root, MPI_Comm сomm) Її вхідні параметри: • sendbuf — адреса буфера передачі;; • sendcount — кількість елементів, що пересилаються кожному процесу (але не сумарна кількість елементів що пересилаються); • sendtype — тип переданих даних; • rcvcount — кількість елементів у буфері прийому; • rcvtype — тип прийнятих даних; • root — ранг передавального процесу; • comm — комунікатор. Вихідний параметр rcvbuf - адреса буфера прийому. Працює ця підпрограма наступним чином. Процес з рангом root ("головний процес") розподіляє вміст буфера передачі sendbuf серед всіх процесів. Вміст буфера передачі розбивається на кілька фрагментів, кожен з яких містить sendcount елементів. Перший фрагмент передається процесу 0, другий процесу 1 і т.п. Аргументи send мають значення тільки на стороні процесу root. Особливості: 1.Оскільки функція MPI_Scatter визначає колективну операцію, виклик цієї функції при виконанні розсилки даних має бути забезпечений в кожному процесі комунікатора. 2.Функція MPI_Scatter передає всім процесам повідомлення однакового розміру. Виконання загальнішого варіанту операції розподілу даних, коли розміри повідомлень для процесів можуть бути різного розміру, забезпечується за допомогою функції MPI_Scatterv. Підпрограма MPI_Gatherвиконує збір повідомлень від інших процесів у буфер головного процеса. int MPI_Gather (void * sendbuf, int sendcount, MPI_Datatype sendtype, void * rcvbuf, int rcvcount, MPI_Datatype rcvtype, int root, MPI_Comm comm) Кожен процес в комунікаторі comm пересилає вміст буфера передачі sendbuf процессу з рангом root. Процес root "склеює" отримані дані в буфері прийому. Порядок склеювання визначається рангами процесів, тобто в результуючому наборі після даних від процессу 0 йдуть дані від процессу 1, потім дані від процессу 2 і т.п. Аргументи rcvbuf, rcvcount і rcvtype відіграють роль тільки на стороні головного процесу. Аргумент rcvcount вказує кількість елементів даних, отриманих від кожного процесу (але не сумарну їх кількість). При виклику підпрограм MPI_Scatter і MPI_Gather з різних процесів варто використовувати спільний головний процес Особливості: 1. Функція MPI_Gather також визначає колективну операцію, і її виклик при виконанні збору даних має бути забезпечений в кожному процесі комунікатора. 2. При використанні функції MPI_Gather збірка даних здійснюється лише в одному процесі. Для здобуття всіх збираних даних на кожному з процесів комунікатора необхідно використовувати функцію збору і розсилки MPI_Allgather 4. Векторні підпрограми розподілу даних Підпрограми MPI_Scatterv і MPI_Gatnerv є розширеними ("векторними") версіями підпрограм MPI_Scatter і MPI_Gather. Вони дозволяють пересилати різним процесам (або збирати від них) різну кількість елементів даних. Завдяки аргументу displs, який задає відстань між елементами, елементи які пересилаються можуть не розташовуватися безперервно в пам'яті головного процесу. Це може виявитися корисним, наприклад, при пересиланні частин масивів. Векторна підпрограма розподілу має наступні параметри: int MPI_Scatterv(void *sendbuf, int *sendcounts, int *displs, MPI_Datatype sendtype, void *rcvbuf, int rcvcount,MPI_Datatype rcvtype, int root, MPI_Comm comm) Вхідні параметри: • sendbuf – адреса буфера передачі; • send counts – цілочисельний одновимірний масив, що містить кількість елементів, переданих кожному процесу (індекс дорівнює рангу адресата). Його довжина дорівнює кількості процесів в комунікаторі; • displs — цілочисельний масив, довжина якого дорівнює кількості процесів в комунікаторі. Елемент з індексом i задає зсув відносно початку буфера передачі. Ранг адресата дорівнює значенню індексу i; • sendtype — тип даних в буфері передачі; • rcvcount — кількість елементів у буфері прийому; • rcvtype — тип даних в буфері прийому; • root — ранг передавального процесу; • comm — комунікатор. Вихідним є адреса буфера прийому rcvbuf. Підпрограма MРІ_Gatherv використовується для збору даних від всіх процесів в заданому комунікаторі та запису їх в буфер прийому із зазначеним зміщенням: іnt MPI_Gatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int *recvcounts, int *displs, MPI_Datatype recvtype, int root, MPI_Comm comm) Список параметрів у цієї підпрограми схожий на список параметрів підпрограми MPI_Scatterv. Виконання загального варіанту операції збору даних, коли розміри повідомлень, що передаються процесами, можуть бути різні, забезпечується за допомогою функції MPI_Allgathev. 5. Операції передачі даних від всіх процесів всім процесам Рисунок 8.6. Загальна схема операції передачі даних від всіх процесів всім процесам (повідомлення показуються позначеннями вигляду i=>j, де i і j є ранги передавальних і приймаючих процесів відповідно) Кожен процес в комунікаторі передає дані з scount елементів кожному процесу (загальний розмір повідомлень, що відправляються, в процесах має бути рівний scount * p елементів, де p є кількість процесів в комунікаторі comm) і приймає повідомлення від кожного процесу. У обмінах, що виконуються підпрограмами MPI_Allgather і MPI_Alltoall, немає головного процесу. Деталі відправки і прийому важливі для усіх процесів, що беруть участь в обміні. Підпрограма MPI_Allgather збирає дані від усіх процесів і розподіляє їх усім процесам (рис. 8.7). Дія цієї підпрограми рівносильна дії послідовності викликів підпрограми MPI_Gather, в кожному з яких як головний використовуються різні процеси. Прийом виконується в усіх завданнях. Буфер прийому послідовно заповнюється повідомленнями від усіх процесів-посилачів. Рисунок 8.7. Схема передачі даних для підпрограми MPI_Allgather int MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *rcvbuf, int rcvcount, MPI_Datatype rcvtype, MPI_Comm comm) Вхідні параметри цієї підпрограми: • sendbuf — початкова адреса буфера передачі; • sendcount — кількість елементів у буфері передачі; • sendtype — тип переданих даних; • rcvcount — кількість елементів, отриманих від кожного процесу; • rcvtype — тип даних у буфері прийому; • comm — комунікатор. Вихідним параметром є адреса буфера прийому (rcvbuf). Блок даних, переданий від i-го процесу, приймається кожним процесом і розміщується в i-м блоці буфера прийому recvbuf. Підпрограма MPІ_Alltoall пересилає дані за схемою "кожен - всім" і аналогічна підпрограмі int MPI_Allgather: int MPI_Alltoall(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *rcvbuf, int rcvcount, MPI_Datatype rcvtype, MPI_Comm comm) Вхідні параметри: • sendbuf — початкова адреса буфера передачі; • sendcount — кількість елементів даних, що пересилаються кожному процесу; • sendtype — тип даних у буфері передачі; • rcvcount — кількість елементів даних, що приймаються від кожного процесу; • rcvtype — тип даних, що приймаються; • comm — комунікатор. Вихідний параметр - адреса буфера прийому rcvbuf. Особливості: 1. Виклик функції MPI_Alltoall при виконанні операції загального обміну даними має бути виконаний в кожному процесі коммунікатора. 2. Варіант операції загального обміну даних, коли розміри передаваних процесами повідомлень можуть бути різні, забезпечується за допомогою функції MPI_Alltoallv. Векторними версіями MPI _ Allgather і MPI _ Alltoall є підпрограми MPI _ Allgatherv і MPI _ Alltoallv. Підпрограма MPI_Allgatherv збирає дані від усіх процесів і пересилає їх усім процесам: int MPI_Allgatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype, void *rcvbuf, int *rcvcounts, int *displs, MPI_Datatype rcvtype, MPI_Comm comm) Її параметри співпадають з параметрами підпрограми MPI _ Allgatner, за винятком додаткового вхідного параметра displs. Це цілочисельний одновимірний масив, кількість елементів в якому дорівнює кількості процесів в комунікаторові. Елемент масиву з індексом i задає зміщення відносно початку буфера прийому recvbuf, в якому розташовуються дані, що приймаються від процесу i. Блок даних, переданий від j- го процесу, приймається кожним процесом і розміщується в j -му блоці буфера прийому. Підпрограма MPI_Alltoallv пересилає дані від усіх процесів усім процесам зі зміщенням: int MPI_Alltoallv(void *sendbuf, int *sendcounts, int *sdispls, MPI_Datatype sendtype, void *rcvbuf, int *rcvcounts, int *rdispls, MPI_Datatype rcvtype, MPI_Comm comm) Її параметри аналогічні параметрам підпрограми MPІ _ Alltoall, окрім двох додаткових параметрів: • sdispls – цілочисельний масив, кількість елементів в якому дорівнює кількості процесів в комунікаторі. Елемент j задає зміщення відносно початку буфера, з якого дані передаються у'-му процесу. • rdispls – цілочисельний масив, кількість елементів в якому дорівнює кількості процесів в комунікаторі. Елемент i задає зміщення відносно початку буфера, в який приймається повідомлення від i- го процесу. Табл. 8.1. Підпрограми розподілу і збору даних Підпрограма Короткий опис MPI_Allgather Збирає дані від всіх процесів і пересилає їх усім процесам MPI Allgatherv Збирає дані від всіх процесів і пересилає їх усім процесам ("векторний" варіант підпрограми MPI_Allgather) MPI Allreduce Збирає дані від всіх процесів, виконує операцію приведення, і результат розподіляє всім процесам MPI_Alltoall Пересилає дані від всіх процесів всім процесам MPI Alltoallv Пересилає дані від всіх процесів всім процесам ("векторний" варіант підпрограми MPI Alltoall) MPI_Gather Збирає дані від групи процесів MPI Gatherv Збирає дані від групи процесів ("векторний" варіант підпрограми MPI Gather) 6. Операції приведення і сканування Операції приведення і сканування належать до категорії глобальних обчислень. У глобальній операції приведення до даних від усіх процесів із заданого комунікатора застосовується операція MPI _ Reduce. Аргументом операції приведення є масив даних – по одному елементу від кожного процесу. Результат такої операції – єдине значення. У підпрограмах глобальних обчислень можливі наступні функції, що передається в підпрограму: • функція MPI, наприклад MPI_SUM; • функція користувача; • обробник функції користувача, який створюється підпрограмою MPI_op_create. Рисунок 8.8. Операція приведення Три версії операції приведення повертають результат: • одному процесу; • усім процесам; • розподіляють вектор результатів між усіма процесами. Операція приведення, результат якої передається одному процесу, виконується при виклику підпрограми MPІ_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 – комунікатор. Рисунок 8.9. Глобальна операція приведення Підпрограма MPI_Reduce застосовує операцію приведення до операндів з buf, а результат кожної операції поміщається у буфер результату result. MPI_Reduce повинна викликатися усіма процесами в комунікаторові comm, a аргументи count, datatype і op в цих викликах повинні співпадати. Функція приведення (ор) не повертає код помилки, тому при виникненні аварійної ситуації або завершується робота усієї програми, або помилка мовчазно ігнорується. У MPI є 12 зумовлених операцій приведення: Табл. 8.2. Список зумовлених операцій приведення MPI Операція Опис МРI_МАХ Визначення максимальних значень елементів одновимірних масивів цілого або дійсного типу MPI_MIN Визначення мінімальних значень елементів одновимірних масивів цілого або дійсного типу MPI_SUM Обчислення суми елементів одновимірних масивів цілого, дійсного або комплексного типу MPI_PROD Обчислення поелементного добутку одновимірних масивів цілого, дійсного або комплексного типу MPI LAND Логічне " І" MPI BAND Бітове " І" MPI_LOR Логічне " АБО" MPI_BOR Бітове " АБО" MPI_LXOR Логічне виключаюче "АБО" MPI_BXOR Бітове виключаюче "АБО" Операція Опис MPI MAXLOC Максимальні значення елементів одновимірних масивів та їх індекси MPI MINLOC Мінімальні значення елементів одновимірних масивів та їх індекси Програміст може визначити і власні глобальні операції приведення. Це робиться за допомогою підпрограми MPI_Op_Сreate: int MPI_Op_Сreate(MPI_User_function *function, int commute, MPI_Op *op) Вхідними параметрами цієї підпрограми є: • function — функція користувача; • commute — прапор, яким присвоюється значення "істина", якщо операція коммутативна (тобто її результат не залежить від порядку операндів). Опис типу функції користувача виглядає наступним чином: typedef void (MPI_User_function) (void*a, void *b, int *len, MPI_Datatype *dtype) Тут операція визначається так: b[I] = a [I] op b[I] для І = 0, ..., len - 1. Після того, як функція користувача зіграла свою роль, її слід анулювати за допомогою підпрограми MPI_op_free: int MPI_Op_free(MPI_Op *ор) По завершенні виклику змінній ор присвоюється значення MPI_OP_NULL. У підпрограми MPI_Reduce є варіанти. Підпрограма MPI_Reduce |