Розподілене програмування з використанням mpi

Параллельное1 додаток складається з декількох процесів або завдань, які виконуються одночасно. Бібліотека підтримки MPI самостійно створює вказане число процесів. Коли програма отримує управління, всі процеси вже створені. Можна вказати, що на одному процесорі слід запустити кілька завдань. В цьому випадку їх праллельное виконання буде емулюватися. Для проведення розрахунків процеси об'єднуються в групи. Кожен процес в групі має унікальний номер. Зазвичай процес з номером 0 вважається головним і управляє іншими.
Особливістю MPI є поняття галузі зв'язку (communication domains). При запуску програми всі процеси поміщаються в одну область зв'язку. Надалі можна створювати нові області на основі існуючих. Всі галузі зв'язку мають незалежну систему нумерації процесів. Програмісту доступний комунікатор - описувач галузі зв'язку. Більшість функцій бібліотеки в качесте одного з параметрів приймають комунікатор, який обмежує сферу їх дії зазначеної областю. Початкова область зв'язку, яка створюється автоматично при старті, використовує комунікатор MPI_COMM_WORLD.
Прототипи більшості функцій бібліотеки оголошені у файлі "mpi.h". Існує кілька функцій, які повинні викликатися будь-розподіленої програмою (див. Табл.1).
Табл. 1. Функції ініціалізації і деініціалізацію бібліотеки MPI

Табл. 1. Функції ініціалізації і деініціалізацію бібліотеки MPI

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

Виконує ініціалізацію бібліотеки. Як параметри отримує покажчики
на стандартні параметри функції main

Нормальне завершення бібліотеки. Функцію необхідно викликати перед завершенням програми


Бібліотека також містить набір інформаційних функцій. У табл. 2 представлені дві з них.
Табл. 2. Інформаційні функції MPI

Табл. 2. Інформаційні функції MPI

MPI_Comm_size (MPI_Comm comm, int * size)

Записує в аргумент size розмір групи, тобто загальна кількість процесів, що виконуються у зазначеній галузі зв'язку comm

MPI_Comm_rank (MPI_Comm comm, int * rank)

У параметр rank поміщає номер процесу, що викликав функцію


Тепер ми можемо написати просту розподілену програму:

/ * Файл first.c * /
#include
/ * Прототипи функцій MPI * /
#include "mpi.h"
int main (int argc, char ** argv)
int myrank, size;
/ * Ініціалізувавши бібліотеку * /
MPI_Init (argc, argv);
/ * Визначаємо число процесів * /
MPI_Comm_size (MPI_COMM_WORLD, size);
/ * Визначаємо номер процесу * /
MPI_Comm_rank (MPI_COMM_WORLD, myrank);
/ * Виводимо отриману інформацію * /
printf ( "Number. size. \ n", myrank, size);
/ * Закриваємо бібліотеку * /
MPI_Finalize ();
return 0;
>

Компіляція і запуск паралельних програм

Для компіляції C-програм, що використовують MPICH, слід використовувати скрипт mpicc. Він запускається з тими ж параметрами, що і компілятор C і додає параметри, необхідні для підключення бібліотеки:

mpicc -o first first.c

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

mpirun -np 4 ./first

Після запуску Ви побачите на екрані наступне:

Number 3 size 4
Number 0 size 4
Number 2 size 4
Number 1 size 4

Порядок виконанню процесів невизначений. Тому і рядки на екрані будуть з'являтися в довільному порядку. Щоб переконатися в цьому, кілька разів запустіть програму.

Передача і прийом повідомлень

Як уже зазначалося, одним із способів взаємодії розподілених процесів є передача повідомлень. MPI (Message Passing Interface) визначає безліч функцій, за допомогою яких завдання можуть приймати і передавати повідомлення. Кожне повідомлення має набір атрибутів - номер процесу-відправника, номер процесу-одержувача і ідентифікатор повідомлення. Ідентифікатором повідомлення є ціле число, яке лежить в діапазоні від 0 до 32767.

Дві найпростіші функції для прийому / передачі представлені в табл. 3.
Табл. 3. Найпростіші функції MPI прийому / передачі

Табл. 3. Найпростіші функції MPI прийому / передачі

int MPI_Send (void * buf, int count, PI_Datatype datatype, int dest, int msgtag, MPI_Comm comm)

Виконує блокує посилку повідомлення з ідентифікатором msgtag, що складається з count елементів типу datatype, процесу з номером dest. Всі елементи повідомлення розташовані поспіль в буфері buf. Значення count може бути нулем. Тип переданих елементів datatype повинен вказуватися за допомогою визначених констант типу. Дозволяється передавати повідомлення самому собі.

int MPI_Recv (void * buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_comm
comm, MPI_Status * status)

Виконує прийом повідомлення з ідентифікатором msgtag від процесу source з блокуванням. Число елементів в прийнятому повідомленні не повинно перевищувати значення count. Якщо число прийнятих елементів менше значення count, то гарантується, що в буфері buf зміняться тільки елементи, відповідні елементам прийнятого повідомлення.

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

Типи даних в MPI

Функцій MPI необхідно вказувати тип переданих даних. Це пов'язано з тим, що процеси розподіленої програми можуть одночасно виконуватися на машинах, що мають різну архітектуру, порядок байт і уявлення даних. Тому всі функції прийому і передачі в MPI оперують не кількістю переданих байт, а кількістю осередків, тип яких задається параметром функції. У MPI використовується спеціальний тип - MPI_Datatype - тип "описатель типів", кожна його змінна описує для MPI один тип. Для кожного базового типу даних, наявного в використовувану мову програмування, в бібліотеці MPI визначені відповідні константи 2.

Наприклад, в табл. 4 представлені константи, визначені для мови C.
Табл. 4. Константи MPI і типи даних C

Табл. 4. Константи MPI і типи даних C


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

/ * Файл mpi2.c * /
#include
#include "mpi.h"
int main (int argc, char ** argv)
int me;
char message [20];
MPI_Status status;

MPI_Init (argc, argv);
MPI_Comm_rank (MPI_COMM_WORLD, me);
if (me == 0) / * це головний процес * /
strcpy (message, "Hello, there");
/ * Пошлемо процесу 1 рядок символів; повідомлення має номер 99 * /
MPI_Send (message, strlen (message) + 1, MPI_CHAR, 1, 99, MPI_COMM_WORLD);
>
else / * це допоміжний процес * /
/ * Чекаємо від процесу 0 повідомлення 99, що містить рядок символів * /
MPI_Recv (message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, status);
printf ( "proc. receiveds:% s \ n", me, message);
>

MPI_Finalize ();
return 0;
>

Програма розрахована тільки на два процеси, тому її треба запускати в такий спосіб:

mpirun -np 2 ./mpi2

На екран вона виведе наступне:

proc 1 receiveds: Hello, there

Якщо скрипту mpirun вказати ключ -l, то перед кожною виведеної рядком буде друкуватися номер процесу, який надрукував цей рядок:

mpirun -l -np 2 ./mpi2
1: proc 1 receiveds: Hello, there

Ми розглянули основи програмування розподілених програм з використанням бібліотеки MPI. У наступній статті будуть розглянуті більш складні і більш потужні функції і приклади.

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

Схожі статті