Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
shpora_ROD_pechat.doc
Скачиваний:
0
Добавлен:
27.12.2019
Размер:
5.2 Mб
Скачать

2. Интерфейс передачи сообщений (Message Passing Interface - mpi). Интерфейс передачи сообщений mpi

Международным стандартом параллельного программирования является модель передачи сообщений MPI (Message Passing Interface). Рекомендуемой бесплатной реализацией MPI является пакет MPICH, разработанный в Арагонской национальной лаборатории.

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

MPI позволяет передавать данные не только от одного процесса к другому, но и поддерживает коллективные операции: широковещательную передачу, разборку-сборку, операции редукции. Вычислительная система может включать разные процессоры, в том числе имеющие различные наборы команд и разное представление данных.

MPI поддерживает создание приложений в модели SPMD (single program multiple data) - одна программа работает в разных процессах со своими данными. Одна и та же функция вызывается на узле-источнике и узлах-приемниках, а тип выполняемой операции (передача или прием) определяется с помощью параметра. Такой вызов делает SPMD-программы существенно компактнее, хотя и труднее для понимания.

MPI - это стандарт на программный инструментарий для обеспечения связи между ветвями параллельного приложения. MPI предоставляет программисту единый механизм взаимодействия ветвей внутри параллельного приложения независимо от машинной архитектуры (однопроцессорные/многопроцессорные с общей/разделяемой памятью), взаимного расположения ветвей (на одном процессоре / на разных процессорах) и API операционной системы ( API = "applications programmers interface" = "интерфейс разработчика приложений" ).

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

Создание параллельной программы включает в себя две основных стадии:

  • последовательный алгоритм подвергается декомпозиции (распараллеливанию), т.е. разбивается на независимо работающие ветви; для взаимодействия в ветви вводятся две дополнительные нематематические операции - прием и передача данных

  • распараллеленный алгоритм записывается в виде программы, в которой операции приема и передачи записываются в терминах конкретной системы связи между ветвями.

В настоящее время разными коллективами разработчиков написано несколько программных пакетов, удовлетворяющих спецификации MPI, в частности: MPICH, LAM, HPVM и так далее. Они выступают базовыми при переносе MPI на новые архитектуры ЭВМ.

Коммуникатор. Основное отличие стандарта MPI от его предшественников - понятие коммуникатора. Все операции синхронизации и передачи сообщений локализуются внутри коммуникатора. С коммуникатором связывается группа процессов. В частности, все коллективные операции вызываются одновременно на всех процессах, входящих в эту группу.

П рактический опыт показывает, что идеально распараллеливаемые задачи, такие как параллельное умножение матриц, можно решать практически на любых сетях, и добавление даже относительно слабых вычислительных узлов дает выигрыш. Другие задачи, в частности решение систем линейных уравнений, более требовательны к коммуникационному оборудованию и качеству реализации передачи сообщений. Именно поэтому тесты для оценки реального быстродействия параллельных вычислительных систем базируются на параллельных аналогах известного пакета Linpack. Так, система линейных уравнений размером 800х800 решается на четырех компьютерах Sun SPARCstation 5, объединенных сетью Ethernet 10 Мбит/c, быстрее, чем на трех; на пяти - приблизительно за то же время, что и на четырех, а добавление шестого компьютера однозначно ухудшает производительность вычислительной системы. Если вместо Fast Ethernet 10 Мбит/c использовать Fast Ethernet 100 Мбит/с, что лишь незначительно увеличивает общую стоимость системы, время, затрачиваемое на коммуникацию, уменьшается почти в 10 раз, а для решения данной задачи можно будет эффективно применять уже десятки рабочих станций.

Система связи, в свою очередь, включает в себя два компонента: программный и аппаратный. С точки зрения программиста базовых парадигм связи две - данные могут передаваться:

  • через разделяемую память, синхронизация доступа ветвей к такой памяти происходит посредством семафоров (разделяемая память и семафоры - SMP-машины);

  • в виде сообщений (индивидуальные интерфейсы с передачей сообщений - MPP-машины).

Общая организация MPI. MPI-программа представляет собой набор независимых процессов, каждый из которых выполняет свою собственную программу (не обязательно одну и ту же). Процессы взаимодействуют друг с другом посредством вызова коммуникационных процедур.

Каждый процесс выполняется в своем собственном адресном пространстве, однако допускается и режим разделения памяти. MPI не специфицирует модель выполнения процесса – это может быть как последовательный процесс, так и многопоточный.

MPI не предоставляет средств для распределения процессов по вычислительным узлам и для запуска их на исполнение. Эти функции возлагаются либо на операционную систему, либо на программиста. MPI не накладывает каких-либо ограничений на то, как процессы будут распределены по процессорам, в частности, возможен запуск MPI-программы с несколькими процессами на однопроцессорной системе.

Для идентификации наборов процессов вводится понятие группы, объединяющей все или какую-то часть процессов. Каждая группа образует область связи, с которой связывается специальный объект – коммуникатор области связи MPI_COMM_WORLD. Процессы внутри группы нумеруются целым числом в диапазоне 0...groupsize-1. Все коммуникационные операции с некоторым коммуникатором будут выполняться только внутри области связи, описываемой этим коммуникатором.

Библиотека. MPI является библиотекой функций межпроцессного обмена сообщениями и содержит около 130 функций, которые делятся на следующие классы: операции точка-точка, операции коллективного обмена, топологические операции, системные и вспомогательные операции.

В принципе, любая параллельная программа может быть написана с использованием всего 6 MPI-функций, а достаточно полную и удобную среду программирования составляет набор из 24 функций. Определение всех именованных констант, прототипов функций и определение типов выполняется подключением файла mpi.h.

Любая прикладная MPI-программа (приложение) должна начинаться с вызова функции инициализации MPI – функции MPI_Init. В результате выполнения этой функции создаются все процессы приложения, и создается область связи, описываемая предопределенным коммуникатором MPI_COMM_WORLD. Процессы в группе упорядочены и пронумерованы от 0 до groupsize-1. Кроме этого создается предопределенный коммуникатор MPI_COMM_SELF, описывающий свою область связи для каждого отдельного процесса.

Прототип функции инициализации:

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

В программах на C каждому процессу при инициализации передаются аргументы функции main, полученные из командной строки. В программах на языке FORTRAN параметр IERROR является выходным и возвращает код ошибки.

Функция завершения MPI-программ MPI_Finalize:

int MPI_Finalize(void);

Она закрывает все MPI-процессы и ликвидирует все области связи.

Функция определения числа процессов в области связи MPI_Comm_size:

int MPI_Comm_size(MPI_Comm comm, int *size);

comm – коммуникатор;

size – число процессов в области связи коммуникатора comm.

Функция возвращает количество процессов в области связи коммуникатора comm.

До создания явным образом групп и связанных с ними коммуникаторов единственно возможными значениями параметра COMM являются MPI_COMM_WORLD и MPI_COMM_SELF, которые создаются автоматически при инициализации MPI.

Функция определения номера процесса MPI_Comm_rank:

int MPI_Comm_rank(MPI_Comm comm, int *rank)

comm – коммуникатор;

rank – номер процесса, вызвавшего функцию.

Функция возвращает номер процесса, вызвавшего эту функцию. Номера процессов лежат в диапазоне 0..size-1 (значение size может быть определено с помощью предыдущей функции). Подпрограмма является локальной.

В минимальный набор входят две функции передачи и приема сообщений.

Функция передачи сообщения MPI_Send:

int MPI_Send(void* buf, int count, MPI_Datatype datatype,

int dest, int tag, MPI_Comm comm);

buf – адрес начала расположения пересылаемых данных;

count – число пересылаемых элементов;

datatype – тип посылаемых элементов;

d est – номер процесса-получателя в группе, связанной с коммуникатором comm;

tag – идентификатор сообщения (аналог типа сообщения функций nread и nwrite PSE nCUBE2);

comm – коммуникатор области связи.

Функция выполняет посылку count элементов типа datatype сообщения с идентификатором tag процессу dest в области связи коммуникатора comm. Переменная buf – это, как правило, массив или скалярная переменная. В последнем случае значение count = 1.

Функция приема сообщения MPI_Recv:

int MPI_Recv(void* buf, int count, MPI_Datatype datatype,

int source, int tag, MPI_Comm comm, MPI_Status *status)

buf – адрес начала расположения принимаемого сообщения;

count – максимальное число принимаемых элементов;

datatype – тип элементов принимаемого сообщения;

source – номер процесса-отправителя;

tag – идентификатор сообщения;

comm – коммуникатор области связи;

status – атрибуты принятого сообщения.

Функция выполняет прием count элементов типа datatype сообщения с идентификатором tag от процесса source в области связи коммуникатора comm.

Приведем с использованием MPI пример программы вычисления числа π. Вычисление числа π сводится к вычислению интеграла по следующей формуле:

где .

Программа на языке С:

#include "mpi.h"

#include <math.h>

int main ( int argc, char *argv[] ) {

int n, myid, numprocs, i; /* число ординат, имя и число процессов*/

double PI25DT = 3.141592653589793238462643; /* для оценки точности вычислений */

double mypi, pi, h, sum, x;

/* mypi – частное значение отдельного процесса, pi – полное значение . */

MPI_Init(&argc, &argv); /* задаются системой*/

MPI_Comm_size(MPI_COMM_WORLD, &numprocs);

MPI_Comm_rank(MPI_COMM_WORLD, &myid);

while (1) {

if (myid == 0) {

printf ("Enter the number of intervals: (0 quits) "); /*ввод числа ординат*/

scanf ("%d", &n);

}

MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

if (n == 0) /* задание условия выхода из программы */

break;

else {

h = 1.0/ (double) n; /* вычисление частного значения . некоторого процесса */

sum = 0.0;

for (i = myid +1; i <= n; i+= numprocs) {

x = h * ( (double)i - 0.5);

sum += (4.0 / (1.0 + x*x));

}

mypi = h * sum; /* вычисление частного значения . некоторого процесса */

MPI_Reduce(&mypi,&pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

/* сборка полного значения . */

if (myid == 0) /* оценка погрешности вычислений */

printf ("pi is approximately %.16f. Error is %.16f\n", pi, fabs(pi - PI25DT));

}

}

MPI_Finalize(); /* выход из MPI */

return 0;

}

Задача - Привести пример кода создания компонента.

int main()

{

printf(rus("\nИнициализация COM ..."));

if ( FAILED( CoInitialize( NULL )))

{

printf(rus("\nОшибка при инициализации COM ..."));

printf(rus("\n\nНажмите любую клавишу для продолжения..."));

getchar();

return -1;

}

printf(rus("\nПолучение интерфейса фабрики классов для класса Plan..."));

hr = CoGetClassObject(CLSID_Plan,CLSCTX_LOCAL_SERVER,NULL,IID_IClassFactory,(void**)&pICF);

if ( FAILED( hr ))

{

printf(rus("\nОшибка при получении указателя на интерфейс фабрики классов. Код ошиибки %X"),hr);

printf(rus("\n\nНажмите любую клавишу для продолжения..."));

getchar();

return -1;

};

IUnknown* pIUnk;

hr = pICF->CreateInstance( NULL, IID_IUnknown, (void**) &pIUnk );

pICF->Release();

if ( FAILED( hr ))

{

printf(rus("\nОшибка при создании экземпляра компонента. Код ошибки %X"),hr);

printf(rus("\n\nНажмите любую клавишу для продолжения..."));

getchar();

return -1;

};

printf(rus("\nЭкземпляр компонента успешно создан "));

printf(rus("\n\nНажмите любую клавишу для продолжения..."));

getchar();

hr = pIUnk->QueryInterface( IID_IPlan, (LPVOID*)&pPlan );

pIUnk->Release();

if ( FAILED( hr ))

{

printf(rus("Ошибка при запросе интерфейса IPlan. Код ошибки %X"),hr);

printf(rus("\n\nНажмите любую клавишу для продолжения..."));

getchar();

getchar();

return -1;

}

DWORD c = 0;

pPlan->Summ(3,4,&c);

printf(rus("Результат - %d"),c);

//char str[256];

//gets(str);

getchar();

return 0;

}

HRESULT __stdcall COMPFactory::CreateInstance(IUnknown* pUnknownOuter,

const IID& iid, void** ppv) {

if (pUnknownOuter != NULL) { // Агрегирование не поддерживается

return CLASS_E_NOAGGREGATION;

}

COMP* pCOMP = new COMP;

if (pCOMP == NULL) {

return E_OUTOFMEMORY;

}

HRESULT hr = pCOMP->QueryInterface(iid, ppv);

// (Если QueryInterface отработает неудачно, компонент удалит сам себя)

pCOMP->Release();

return hr;

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]