Обмін даними з використанням MPI. Робота з бібліотекою MPI з прикладу Intel® MPI Library. Пересилання різнотипних даних. Основні функції MPI

Функції MPI

Похідний тип, Операції, Типи даних

Bsend Buffer_attach Get_count ANY_SOURCE Sendrecv_replace ANY_TAG Probe

Allgetherv Alltoall Alltoallv Reduce Rduce_scatter Scan

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

MPI_Type_contiguous, MPI_Type_vector, MPI_Type_hvector, MPI_Type_indexed, MPI_Type_hindexed, MPI_Type_struct.

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

Коли похідний тип стає непотрібним, він знищується функцією MPI_Type_free.

1) MPI_Init – функція ініціалізації. В результаті виконання цієї функції створюється група процесів, в яку містяться всі процеси програми, і створюється область зв'язку, що описується зумовленим комунікатором MPI_COMM_WORLD.

MPI_Type_commit - реєстрація типу, MPI_Type_free – знищення типу

int MPI_Init(int *argc, char***argv);

2) MPI_Finalize - Функція завершення MPI програм. Функція закриває всі MPI-процеси та ліквідує всі сфери зв'язку.

int MPI_Finalize(void);

3) Функція визначення кількості процесів у галузі зв'язку MPI_Comm_size . Функція повертає кількість процесів у галузі зв'язку комунікатора comm.

int MPI_Comm_size(MPI_Comm comm, int *size);

4) Функція визначення номера процесу MPI_Comm_rank . Функція повертає номер процесу, що спричинив цю функцію. Номери процесів лежать у діапазоні 0..size-1.

int MPI_Comm_rank(MPI_Comm comm, int *rank);

5) Функція надсилання повідомлення MPI_Send. Функція виконує посилку count елементів типу datatype повідомлення з ідентифікатором tag процесу dest у зв'язку комунікатора comm.

int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);

6) Функція отримання повідомлення MPI_Recv. Функція виконує прийом count елементів типу datatype повідомлення з ідентифікатором tag від процесу source у сфері зв'язку комунікатора comm.

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

7) Функція відліку часу (таймер) MPI_Wtime. Функція повертає астрономічний час у секундах, який минув з деякого моменту в минулому (точки відліку).

double MPI_Wtime(void)

Функції передачі повідомлень між процесами поділяються на:

Префікс S (synchronous)

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

Префікс B (buffered)

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

Префікс R(ready)

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

Префікс I (immediate)

відноситься до неблокуючих операцій.

Структура MPI_Status

Після прочитання повідомлення деякі параметри можуть виявитися невідомими, а саме: кількість лічених елементів, ідентифікатор повідомлення та адреса відправника. Цю інформацію можна отримати за допомогою status. Змінні status мають бути явно оголошені у MPI-програмі. У мові C status - це структура типу MPI_Status із трьома полями MPI_SOURCE, MPI_TAG, MPI_ERROR.

8) Для визначення числа фактично одержаних елементів повідомлення необхідно використовувати спеціальну функцію MPI_Get_count .

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

9) Визначити параметри отриманого повідомлення без його читання можна за допомогою функції MPI_Probe. int MPI_Probe (int source, int tag, MPI_Comm comm, MPI_Status *status);

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

int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, MPI_Datatypeа recvtag, MPI_Comm comm, MPI_Status

11) Функція перевірки завершення неблокуючої операції MPI_Test.

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);

Це локальна операція, що не блокує. Якщо операція завершена, пов'язана із запитом request, повертається flag = true, а status містить інформацію про завершену операцію. Якщо операція, що перевіряється, не завершена, повертається flag = false, а значення status у цьому випадку не визначено.

12) Функція зняття запиту без очікування завершення неблокуючої операції MPI_Request_free.

int MPI_Request_free(MPI_Request *request);

Параметр request встановлюється значення MPI_REQUEST_NULL.

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

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

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

14) При необхідності отримання повідомлення від будь-якогопроцесу-відправника для параметра source може бути вказано значення MPI_ANY_SOURCE

15) При необхідності отримання повідомлення з будь-яким тегом для параметра tag може бути вказано значення MPI_ANY_TAG

16) Параметр status дозволяє визначити низку характеристик прийнятого повідомлення:

- status.MPI_SOURCE – рангпроцесу-відправника прийнятого повідомлення,

- status.MPI_TAG – тег прийнятого повідомлення.

17) Функція

MPI_Get_coun t(MPI_Status *status, MPI_Datatype type, int *count)

повертає у змінній count кількість елементів типу type у прийнятому повідомленні.

18) Операції передачі від усіх процесів одному процесу. У цій операції над збираються

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

int MPI_Reduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype типу, MPI_Op op,int root,MPI_Comm comm)

19) Синхронізація процесів, тобто. одночасне досягнення процесами тих чи інших точок процесу обчислень забезпечується за допомогою функції MPI: int MPI_Barrier (MPI_Comm comm); Функція MPI_Barrier визначає колективну операції і, тим самим, при використанні повинна викликатися всіма процесами комун+ікатора. Під час виклику функції MPI_Barrier

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

20) Для використання буферизованого режиму передачі повинен бути створений і переданий буфер пам'яті MPI

для буферизації повідомлень – функція, що використовується для цього, має вигляд: int MPI_Buffer_attach (void *buf, int size),

- bufбуфер пам'яті для буферизації повідомлень,

- size - розмір буфера.

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

int MPI_Buffer_detach (void *buf, int *size).

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

int MPI_Sendrecv (void *sbuf,int scount,MPI_Datatype stype,int dest,int stag, void *rbuf,int rcount,MPI_Datatype

rtype,int source,int rtag, MPI_Comm comm, MPI_Status *status)

23) Коли повідомлення мають однаковий тип, у MPI є можливість використання єдиного буфера: intMPI_Sendrecv_replace (void *buf, int count, MPI_Datatype type, int dest,

int stag, int source, int rtag, MPI_Comm comm, MPI_Status* status)

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

int MPI_Scatter (void *sbuf,int scount,MPI_Datatype stype,

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

int MPI_Gather (void *sbuf,int scount,MPI_Datatype stype,

void *rbuf,int rcount,MPI_Datatype rtype, int root, MPI_Comm comm)

26) Слід зазначити, що при використанні функції MPI_Gather збирання даних здійснюється лише

одному процесі. Для отримання всіх даних, що збираються на кожному з процесів комунікатора

необхідно використовувати функцію збору та розсилки:

int MPI_Allgather (void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm)

27) Передача даних від усіх процесів усім процесам є найбільш загальною операцією передачі (див. рис. 4.6). Виконання цієї операції може бути забезпечене за допомогою функції:

int MPI_Alltoall (void *sbuf,int scount,MPI_Datatype stype, void *rbuf,int rcount,MPI_Datatype rtype,MPI_Comm comm)

28) Функція MPI_Reduce забезпечує отримання результатів редукції даних

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

int MPI_Allreduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

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

int MPI_Scan (void *sendbuf, void *recvbuf,int count,MPI_Datatype типу, MPI_Op op,MPI_Comm comm).

Загальна схема виконання функції MPI_Scan показано на рис. 4.7. Елементи одержуваних повідомлень є результатами обробки відповідних елементів повідомлень, що передаються процесами, при цьому для отримання результатів на процесі з рангом i, 0≤i

30) Початкове значення змінної bufpos має бути сформоване до початку упаковки і далі встановлюється функцією MPI_Pack. Виклик функції MPI_Pack здійснюється послідовно для упакування всіх необхідних даних.

int MPI_Pack_size (int count, MPI_Datatype type, MPI_Comm comm, int *size)

31) Після упаковки всіх необхідних даних, підготовлений буфер може бути використаний у функціях передачі даних із зазначенням типу MPI_PACKED.

Після отримання повідомлення з типом MPI_PACKED дані можуть бути розпаковані за допомогою функції:

int MPI_Unpack (void *buf, int bufsize, int *bufpos, void *data, int count, MPI_Datatype type, MPI_Comm comm)

Complex Instruction Set Computer

CISC (англ. Complex instruction set computing, або англ. complex instruction set computer -

комп'ютер з повним набором команд) - концепція проектування процесорів, яка характеризується наступним набором властивостей:

порівняно невелика кількість регістрів загального призначення;

· велика кількість машинних команд, деякі з яких навантажені семантично аналогічно операторам високорівневих мов програмування та виконуються за багато тактів;

· велика кількість методів адресації;

· велика кількість форматів команд різної розрядності;

· переважання двоадресного формату команд;

· наявність команд обробки типурегістр-пам'ять.

Недоліки:

висока вартість апаратної частини; складності з розпаралелювання обчислень.

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

Reduced Instruction Set Computer

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

Такі комп'ютери засновані на архітектурі, що відокремлює команди обробки від команд роботи з пам'яттю, і наголошували на ефективну конвеєрну обробку. Система команд розроблялася так, щоб виконання будь-якої команди займало невелику кількість машинних тактів (переважно один машинний такт). Сама логіка виконання команд з метою підвищення продуктивності орієнтувалася на апаратну, а не мікропрограмну реалізацію. Щоб спростити логіку декодування команд, використовувалися команди фіксованої довжини.

і фіксованого формату.

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

У процесорі передбачено механізм динамічного прогнозування напряму переходів. З цією

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

Структурні конфлікти та способи їх мінімізації

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

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

Мінімізація: конвеєр припиняє виконання однієї з команд доти, доки не стане доступним потрібний пристрій.

Конфлікти за даними, зупинки конвеєра та реалізація механізму обходів

Одним із факторів, який істотно впливає на продуктивність конвеєрних систем, є міжкомандні логічні залежності. Конфлікти за даними виникають у тому випадку, коли застосування конвеєрної обробки може змінити порядок звернень за операндами так, що цей порядок відрізнятиметься від порядку, який спостерігається під час послідовного виконання команд на неконвеєрній машині. Проблема, поставлена ​​в цьому прикладі, може бути вирішена за допомогою досить простої апаратної техніки, яка називається пересиланням або просуванням даних (data forwarding), обходом (data bypassing), іноді короткою (short-circuiting).

Конфлікти за даними, що призводять до припинення конвеєра

Натомість нам потрібна додаткова апаратура, звана апаратурою внутрішніх блокувань конвеєра (pipeline interlook), щоб забезпечити коректне виконання прикладу. Взагалі такого роду апаратура виявляє конфлікти та зупиняє конвеєр доти, доки існує конфлікт. У цьому випадку ця апаратура зупиняє конвеєр, починаючи з команди, яка хоче використовувати дані в той час, коли попередня команда, результат якої є операндом для нашої, виробляє цей результат. Ця апаратура викликає припинення конвеєра або появу "бульбашки" так само, як і у разі структурних конфліктів.

Буфер прогнозування умовних переходів

Буфер прогнозування умовних переходів є невеликою пам'яттю, що адресується за допомогою молодших розрядів адреси команди переходу. Кожен осередок цієї пам'яті містить один біт, який говорить про те, чи був попередній перехід виконуваним чи ні. Це найпростіший вид такого роду буфера. У ньому відсутні теги, і він виявляється корисним лише скорочення затримки переходу у разі, якщо ця затримка більше, ніж час, необхідне обчислення значення цільового адреси переходу. Буфер прогнозування переходів може бути реалізований у вигляді невеликої спеціальної кеш-пам'яті, доступ до якої здійснюється за допомогою адреси команди під час стадії вибірки команди в конвеєрі (IF), або як пара бітів, пов'язаних з кожним блоком кеш-пам'яті команд і вибираються з кожною командою.

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

Основні принципи та приклад
Як приклад використовуватиметься розрахунок експоненти (e). Один із варіантів її знаходження – ряд Тейлора:
e^x=∑((x^n)/n!), де підсумовування походить від n=0 до нескінченності.

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

Кількість доданків, яке розраховуватиметься в кожному окремо взятому процесорі, залежить як і від довжини інтервалу n, так і від наявної кількості процесорів k, які зможуть брати участь у процесі обчислення. Так, наприклад, якщо довжина інтервалу n=4, а обчисленнях беруть участь п'ять процесорів (k=5), то з першого по четвертий процесори отримають по одному доданку, а п'ятий буде не задіяний. Якщо ж n=10, а k=5, кожному процесору дістанеться по два складові для обчислення.

Спочатку перший процесор за допомогою функції широкомовної розсилки MPI_Bcast відправляє іншим значення заданої користувачами змінної n. У загальному випадку функція MPI_Bcast має такий формат:
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm), де buffer – це адреса буфера з елементом, сount – кількість елементів, datatype – відповідний тип даних у MPI, root – ранг головного процесора, який займається пересиланням, а comm-ім'я комунікатора.
У моєму випадку в ролі головного процесора, як уже говорилося, виступатиме перший процесор з рангом 0.

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

У ході виконання циклу доданки додаватимуться в окрему змінну і, після його завершення, отримана сума відправиться в головний процесор. Для цього буде використано функцію операції приведення MPI_Reduce. Загалом вона виглядає так:
int MPI_Reduce(void *buf, void *result, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)

Вона об'єднує елементи вхідного буфера кожного процесу групи, використовуючи операцію op, і повертає об'єднане значення у вихідний буфер процесу з номером root. Результатом такої операції буде єдине значення, завдяки чому функція приведення і отримала свою назву.

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

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

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

Алгоритм виконання коду
1. З візуальної оболонки до програми передається значення числа n, яке потім за допомогою функції широкомовного розсилання відправляється по всіх процесорах.
2. При ініціалізації першого головного процесора запускається таймер.
3. Кожен процесор виконує цикл, де значенням збільшення є кількість процесорів у системі. У кожній ітерації циклу обчислюється доданок і сума таких доданків зберігається у змінну drobSum.
4. Після завершення циклу кожен процесор підсумовує значення drobSum до змінної Result, використовуючи для цього функцію приведення MPI_Reduce.
5. Після завершення розрахунків на всіх процесорах, перший головний процесор зупиняє таймер і відправляє в потік виведення значення змінної Result, що вийшло.
6. У потік виведення відправляється також і відміряне нашим таймером значення часу мілісекундах.
Лістинг коду
Програма написана на С++, вважатимемо що аргументи до виконання передаються із зовнішньої оболонки. Код виглядає так:
#include "mpi.h"
#include
#include
using namespace std;

double Fact(int n)
{
if (n==0)
return 1;
else
return n*Fact(n-1);
}

int main(int argc, char *argv)
{
SetConsoleOutputCP(1251);
int n;
int myid;
int numprocs;
int i;
int rc;
long double drob, drobSum = 0, Result, sum;
double startwtime = 0.0;
double endwtime;

N = atoi (argv);

if (rc= MPI_Init(&argc, &argv))
{
cout<< "Помилка запуску, виконання зупинено" << endl;
MPI_Abort(MPI_COMM_WORLD, rc);
}

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);

if (myid == 0)
{

Startwtime = MPI_Wtime();
}
MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

for (i = myid; i<= n; i += numprocs)
{
drob = 1/Fact(i);
drobSum += drob;
}

MPI_Reduce(&drobSum, &Result, 1, MPI_LONG_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
cout.precision(20);
if (myid == 0)
{
cout<< Result << endl;
endwtime = MPI_Wtime();
cout<< (endwtime-startwtime)*1000 << endl;
}

MPI_Finalize();
return 0;
}


* Цей source code був highlighted with Source Code Highlighter.
Висновок
Таким чином ми отримали просту програму для підрахунку експоненти з використанням відразу кількох процесорів. Напевно, вузьким місцем є зберігання самого результату, тому що зі збільшенням кількості розрядів вміщувати значення з використанням стандартних типів банально не вийде і це місце вимагає опрацювання. Мабуть, досить раціональним рішенням є запис результату у файл, хоча, зважаючи на суто навчальну функцію цього прикладу, особливо на цьому увагу можна не акцентувати.

У цій нотатці показано як встановити MPI, підключити його до Visual Studio, а потім використовувати із заданими параметрами (числом обчислювальних вузлів). У статті використовують Visual Studio 2015, т.к. саме з нею виникали проблеми у моїх студентів (ця нотатка написана студентами для студентів), проте ймовірно, інструкція підійде і для інших версій.

Крок 1:
Необхідно встановити пакет HPC Pack 2008 SDK SP2 (у вашому випадку може бути вже інша версія), доступний на офіційному сайті Microsoft. Розрядність пакета та системи повинні відповідати.

Крок 2:
Необхідно налаштувати шляхи, для цього переходимо у вкладку Debug - Properties:

"C:\Program Files\Microsoft HPC Pack 2008 SDK\Include"

У полі Library Directories:

"C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\amd64"

У полі з бібліотеками, якщо стоїть 32-розрядна версія, замість amd64 потрібно прописати i386.

Msmpi.lib

:

Крок 3:

Для налаштування запуску необхідно перейти у вкладку Debugging та в полі Command вказати:

"C:\Program Files\Microsoft HPC Pack 2008 SDK\Bin\mpiexec.exe"

У полі Command Arguments вказати, наприклад,

N 4 $(TargetPath)

Число 4 вказує на кількість процесів.

Для запуску програми необхідно підключити бібліотеку

Шлях до проекту не повинен містити кирилицю. При виникненні помилок можна скористатися Microsoft MPI, доступний на сайті Microsoft.

Для цього після встановлення достатньо прописати в полі Command вкладки Debugging шлях:

"C:\Program Files\Microsoft MPI\Bin\mpiexec.exe"

Також перед запуском програми не забудьте вказати її розрядність:

Приклад запуску програми з MPI:

#include #include using namespace std; int main(int argc, char **argv) ( int rank, size; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); cout<< "The number of processes: " << size << " my number is " << rank << endl; MPI_Finalize(); return 0; }

Робота програми на 2 вузлах:

Запуск MPI-програми на обчислювальному кластері можливий лише через систему пакетної обробки завдань. Для спрощення запуску та постановки в чергу паралельної програми передбачено спеціальний скрипт mpirun. Наприклад, mpirun -np 20./first.exe запустить паралельну програму first.exe на 20 процесорах, тобто. на 5 вузлах. (Кожен вузол має 2 двоядерні процесори). Варто звернути увагу, що для запуску модуля, що виконується, що знаходиться в поточній директорії ($ pwd) необхідно явно вказати шлях «./» Ряд реалізацій MPI-1 надає команду запуску для програм MPI, яка має форму mpirun<аргументы mpirun><программа><аргументы программы>

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

У той час як стандартизований механізм запуску покращує застосовність MPI, діапазон середовищ настільки різноманітний (наприклад, не може бути навіть інтерфейсу командного рядка), що MPI не може прийняти під мандат такий механізм. Натомість, MPI визначає команду запуску mpiexec і рекомендує, але не вимагає, як порада розробникам. Однак, якщо реалізація забезпечує команду mpiexec, вона повинна мати форму, описану нижче: mpiexec -n <программа>

буде принаймні один спосіб запустити<программу>з початковим MPI_COMM_WORLD, чия група містить процесів. Інші аргументи mpiexec можуть залежати від реалізації.

Приклад 4.1 Запуск 16 екземплярів myprog на поточній або заданій за замовчуванням машині:

mpiexec -n 16 myprog

3. Напишіть програму паралельного обчислення певного інтеграла від функції 2*(x+2*x*x/1200.0) в інтервалі .

Метод лівих прямокутників

double f(double x)

(Return 2*(x+2*x*x/1200);) // iskomyi integral

int main(int argc,char **argv)

MPI_Status status;

MPI_Init(&argc,&argv);

MPI_Comm_rank(MPI_COMM_WORLD,&rank);

MPI_Comm_size(MPI_COMM_WORLD,&size);

int n=1000,i,d; // 1000 - uzly

float a=0, b=1, h=(b-a)/n,s=0,r=0; //a і b-nachalo i konec otrezka

if (rank!=size-1) // schitaut vse processy, крім poslednego

( for (i = rank * d; i<(rank+1)*d; i++) { s=s+h*f(a+i*h); }

MPI_Send(&s,1,MPI_FLOAT,size-1,1,MPI_COMM_WORLD);)

( for (i=0; i

( MPI_Recv(&s,1,MPI_FLOAT,i,1,MPI_COMM_WORLD, &status); r+=s; ) )

MPI_Finalize();)

Сурак

1. Shared & distributed memory архітектури.

Розподілена загальна пам'ять (DSM - Distributed Shared Memory)

Традиційно розподілені обчислення базуються на моделі передачі повідомлень, де дані передаються від процесора до процесора у вигляді повідомлень. Віддалений виклик процедур фактично є тією ж моделлю (або дуже близькою). DSM - віртуальний адресний простір, що поділяється всіма вузлами (процесорами) розподіленої системи. Програми отримують доступ до даних DSM приблизно так само, як вони працюють з даними у віртуальній пам'яті традиційних ЕОМ. У системах з DSM дані переміщаються між локальними пам'ятями різних комп'ютерів, аналогічно тому, як вони переміщаються між оперативною та зовнішньою пам'яттю одного комп'ютера. Конфігурація - з розподіленою пам'яттю, що розділяється, являє собою варіант розподіленої пам'яті. Тут усі вузли, що складаються з одного або кількох процесорів, підключених за схемою SMP, використовують загальний адресний простір. Відмінність цієї конфігурації від машини з розподіленою пам'яттю полягає в тому, що тут будь-який процесор може звернутися до будь-якої ділянки пам'яті. Однак, час звернення до різних ділянок пам'яті для кожного процесора по-різному в залежності від того, де ділянка фізично розташована в кластері. Тому такі конфігурації ще називають машинами з неоднорідним доступом до пам'яті NUMA (non-uniform memory access).

Відмінності MPI та PVM.

Система PVM (Parallel Virtual Machine) була створена для об'єднання кількох зв'язаних мережею робочих станцій у єдину віртуальну паралельну обчислювальну машину. Система є надбудовою над операційною системою UNIX і використовується на різних апаратних платформах, включаючи і системи з масовим паралелізмом. Найбільш поширені зараз системи паралельного програмування з урахуванням MPI (Message Parsing Interface). Ідея MPI вихідно проста і очевидна. Вона передбачає подання паралельної програми як безліч паралельно виконуються процесів, взаємодіючих друг з одним у ході виконання передачі з допомогою комунікаційних процедур. Вони й становлять бібліотеку MPI. Однак належна реалізація MPI для забезпечення міжпроцесорних комунікацій виявилася досить складною. Така складність пов'язана з необхідністю досягнення високої продуктивності програм, необхідністю використовувати численні ресурси мультикомп'ютера, і, як наслідок, великою різноманітністю в реалізації комунікаційних процедур залежно від режиму обробки даних.