Топ новостей


РЕКЛАМА



Календарь

НОУ ІНТУЇТ | лекція | Введення в технології паралельного програмування (MPI)

  1. 5.2.2. Визначення часу виконання MPI-програми Практично відразу ж після розробки перших паралельних...
  2. 5.2.3.1 Передача даних від одного процесу всім процесам програми
  3. 5.2.3.2 Передача даних від всіх процесів одному процесу. операції редукції
  4. 5.2.3.3 Синхронізація обчислень

5.2.2. Визначення часу виконання MPI-програми

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

Отримання часу поточного моменту виконання програми забезпечується за допомогою функції:

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

double t1, t2, dt; t1 = MPI_Wtime (); ... t2 = MPI_Wtime (); dt = t2 - t1;

Точність вимірювання часу також може залежати від середовища виконання паралельної програми. Для визначення поточного значення точності може бути використана функція:

що дозволяє визначити час в секундах між двома послідовними показниками часу апаратного таймера використовуваної комп'ютерної системи.

5.2.3. Початок роботи з колективними операціями передачі даних

Функції MPI_Send і MPI_Recv, розглянуті в п. 5.2.1, забезпечують можливість виконання парних операцій передачі даних між двома процесами паралельної програми. Для виконання комунікаційних колективних операцій, в яких беруть участь всі процеси комунікатора, в MPI передбачений спеціальний набір функцій. В даному підрозділі будуть розглянуті три такі функції, широко використовувані навіть при розробці порівняно простих паралельних програм; повне ж уявлення колективних операцій буде дано в підрозділі 5.4.

Для демонстрації прикладів застосування розглянутих функцій MPI буде використовуватися навчальне завдання підсумовування елементів вектора x (див. Підрозділ 2.5):

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

5.2.3.1 Передача даних від одного процесу всім процесам програми

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

MPI_Comm_size (MPI_COMM_WORLD, & ProcNum); for (i = 1; i <ProcNum; i ++) MPI_Send (& x, n, MPI_DOUBLE, i, 0, MPI_COMM_WORLD);

Однак таке рішення буде вкрай неефективним, оскільки повторення операцій передачі призводить до підсумовування витрат (латентностей) на підготовку переданих повідомлень. Крім того, як показано в розділі 3, дана операція може бути виконана всього за Однак таке рішення буде вкрай неефективним, оскільки повторення операцій передачі призводить до підсумовування витрат (латентностей) на підготовку переданих повідомлень ітерацій передачі даних.

Досягнення ефективного виконання операції передачі даних від одного процесу всім процесам програми (широкомовлення даних) може бути забезпечено за допомогою функції MPI:

int MPI_Bcast (void * buf, int count, MPI_Datatype type, int root, MPI_Comm comm),

де buf, count, type - буфер пам'яті з відправляється повідомленням (для процесу з рангом 0), і для прийому повідомлень для всіх інших процесів, root - ранг процесу, що виконує розсилку даних, comm - комунікатор, в рамках якого відбуватиметься передача даних.

Функція MPI_Bcast здійснює розсилку даних з буфера buf, що містить count елементів типу type з процесу, що має номер root, всім процесам, що входять в комунікатор comm (див. Мал. 5.1 ).


Мал.5.1.

Загальна схема операції передачі даних від одного процесу всім процесам

Слід зазначити:

  1. Функція MPI_Bcast визначає колективну операцію і, тим самим, при виконанні необхідних розсилок даних виклик функції MPI_Bcast повинен бути здійснений всіма процесами зазначених вище комунікатора (див. Далі приклад програми),
  2. Указується в функції MPI_Bcast буфер пам'яті має різне призначення в різних процесах. Для процесу з рангом root, з якого здійснюється розсилка даних, в цьому буфері має перебувати розсилається повідомлення. Для всіх інших процесів вказується буфер призначений для прийому переданих даних.

Наведемо програму для вирішення навчальної задачі підсумовування елементів вектора з використанням розглянутої функції.

#include <math.h> #include <stdio.h> #include <stdlib.h> #include "mpi.h" int main (int argc, char * argv []) {double x [100], TotalSum, ProcSum = 0,0; int ProcRank, ProcNum, N = 100; MPI_Status Status; // ініціалізація MPI_Init (& argc, & argv); MPI_Comm_size (MPI_COMM_WORLD, & ProcNum); MPI_Comm_rank (MPI_COMM_WORLD, & ProcRank); // підготовка даних if (ProcRank == 0) DataInitialization (x, N); // розсилка даних на всі процеси MPI_Bcast (x, N, MPI_DOUBLE, 0, MPI_COMM_WORLD); // обчислення часткової суми на кожному з процесів // на кожному процесі підсумовуються елементи вектора x від i1 до i2 int k = N / ProcNum; int i1 = k * ProcRank; int i2 = k * (ProcRank + 1); if (ProcRank == ProcNum-1) i2 = N; for (int i = i1; i <i2; i ++) ProcSum = ProcSum + x [i]; // збірка часткових сум на процесі з рангом 0 if (ProcRank == 0) {TotalSum = ProcSum; for (int i = 1; i <ProcNum; i ++) {MPI_Recv (& ProcSum, 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & Status); TotalSum = TotalSum + ProcSum; }} Else // всі процеси відсилають свої часткові суми MPI_Send (& ProcSum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); // вивід результату if (ProcRank == 0) printf ( "\ nTotal Sum =% 10.2f", TotalSum); MPI_Finalize (); } 5.2. Паралельна програма підсумовування числових значень

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

5.2.3.2 Передача даних від всіх процесів одному процесу. операції редукції

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

int MPI_Reduce (void * sendbuf, void * recvbuf, int count, MPI_Datatype type, MPI_Op op, int root, MPI_Comm comm),

де sendbuf - буфер пам'яті з відправляється повідомленням, recvbuf - буфер пам'яті для результуючого повідомлення (тільки для процесу з рангом root), count - кількість елементів в повідомленнях, type - тип елементів повідомлень, op - операція, яка повинна бути виконана над даними, root - ранг процесу, на якому повинен бути отриманий результат, comm - комунікатор, в рамках якого виконується операція.

Як операцій редукції даних можуть бути використані визначені в MPI операції - см. табл. 5.2 .

Таблиця 5.2. Базові (пpедопpеделенние) типи операцій MPI для функцій редукції даних Операція Опис MPI_MAX Визначення максимального значення MPI_MIN Визначення мінімального значення MPI_SUM Визначення суми значень MPI_PROD Визначення добутку значень MPI_LAND Виконання логічної операції "І" над значеннями повідомлень MPI_BAND Виконання бітової операції "І" над значеннями повідомлень MPI_LOR виконання логічної операції "АБО" над значеннями повідомлень MPI_BOR виконання бітової операції "АБО" над значеннями повідомлень MPI_LXOR виконання логічної операції исключ ючої "АБО" над значеннями повідомлень MPI_BXOR Виконання бітової операції виключає "АБО" над значеннями повідомлень MPI_MAXLOC Визначення максимальних значень і їх індексів MPI_MINLOC Визначення мінімальних значень і їх індексів

Крім цього стандартного набору операцій можуть бути визначені і нові додаткові операції безпосередньо самим користувачем бібліотеки MPI - див., Наприклад, Немнюгин і Стесік (2002), Group, et al. (1994), Pacheco (1996).

Загальна схема виконання операції збору та обробки даних на одному процесорі показана на Мал. 5.2 . Елементи одержуваного повідомлення на процесі root є результати обробки відповідних елементів переданих процесами повідомлень, тобто Загальна схема виконання операції збору та обробки даних на одному процесорі показана на   Мал де є операція, що задається при виконанні функції MPI_Reduce (для пояснення на Мал. 5.3 показаний приклад виконання операції редукції даних).


Мал.5.2.

Загальна схема операції збору і обробки на одному процесі даних від всіх процесів

Слід зазначити:

  1. Функція MPI_Reduce визначає колективну операцію і, тим самим, виклик функції повинен бути виконаний всіма процесами зазначених вище комунікатора, всі виклики функції повинні містити однакові значення параметрів count, type, op, root, comm,
  2. Передача повідомлень повинна бути виконана всіма процесами, результат операції буде отримано лише процесом з рангом root,
  3. Рятувальна операція редукції здійснюється над окремими елементами переданих повідомлень. Так, наприклад, якщо повідомлення містять по два елементи даних і виконується операція підсумовування MPI_SUM, то результат також буде складатися з двох значень, перше з яких буде містити суму перших елементів всіх відправлених повідомлень, а друге значення дорівнюватиме сумі друге елементів повідомлень відповідно.

Мал.5.3.

Приклад виконання операції редукції при підсумовуванні даних, що пересилаються для трьох процесів (в кожному повідомленні 4 елементи, повідомлення збираються на процесі з рангом 2)

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

// збірка часткових сум на процесі з рангом 0 MPI_Reduce (& ProcSum, & TotalSum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

5.2.3.3 Синхронізація обчислень

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

Синхронізація процесів, тобто одночасне досягнення процесами тих чи інших точок процесу обчислень, забезпечується за допомогою функції MPI:

int MPI_Barrier (MPI_Comm comm);

Функція MPI_Barrier визначає колективну операції і, тим самим, при використанні повинна викликатися всіма процесами використовуваного комунікатора. При виконанні функції MPI_Barrier виконання процесу блокується, продовження обчислень процесу відбудеться тільки після виклику функції MPI_Barrier усіма процесами комунікатора.


Реклама



Новости