Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Библиотека MPI [Электронный ресурс] (90

..pdf
Скачиваний:
5
Добавлен:
15.11.2022
Размер:
546.43 Кб
Скачать

/*Идентификаторысообщений*/

#define tagFloatData 1 #define tagDoubleData 2

/*Этотмакросвведендляудобства, *онпозволяетуказыватьдлинумассиваколичествея чеек

*/

#define ELEMS(x) ( sizeof(x) / sizeof(x[0]) )

int main( int argc, char **argv )

{

int size, rank, count; float floatData[10]; double doubleData[20]; MPI_Status status;

/*Инициализируембиблиотеку*/

MPI_Init( &argc, &argv );

Узнаемколичествозад/* апущенномприложении*/

MPI_Comm_size( MPI_COMM_WORLD, &size );

/*исв...ойбствном:отдое0рнный(size

-1) */

MPI_Comm_rank( MPI_COMM_WORLD, &rank );

/*пользовательдолжензапуститьровнодвезадачи,

 

иначеошибка*/

 

 

if( size != 2 ) {

 

/*задачасномеромсообщает0 пользоватошибк*/ елю

 

 

if( rank==0 )

 

 

 

printf("Error: two processes required",

/*Всезадачи

"instead of %d, abort\n", size );

-абонентыбластисвя

зиMPI_COMM_WORLD

будутстоять* ,показадачане0выведетсообщение.

 

*/

MPI_Barrier( MPI_COMM_WORLD );

 

/*Безточкисинхронизацииоднаможетдач

 

вызв*

ать MPIраньше,чемуспеетAbortотработатьprintf()

опринудительнозавершит

в*задаче0, немедленнMPI Abort

все*задачисообщениевывбудетдено

 

*/

 

 

 

/*всезадачиаварийнозавершаютработу*/

 

 

MPI_Abort(

 

 

ОписательобластисвязMPI,на COMM_WORLD, /*

 

 

 

которуюраспространяетсядействиеошибки*/

 

ЦелочисленныйкMPIшибкид*/ERR_OTHER ); /*

 

}

return -1;

 

 

 

 

if( rank==0 ) {

 

Задачачто0 /*

-тотакоеперезада1 етче*/

 

11

MPI_Send(

 

ссива*/

floatData,адреспередаваемогома /* 1)

5,

/*сколько:2)яч5,т.е.ек

 

floatData[0]..floatData[4] */

типячеек*/MPI_FLOAT, /* 3)

кому:задаче1, */ /* 4) tagFloatData, /*идентификатор5)сообщения*/ MPI_COMM_WORLDописательобласти);связ/*, 6)

черезкоторую

происходитпередача*/ /*иещеоднапере:данныеачаругоготипа*/

MPI_Send( doubleData, 6, MPI_DOUBLE, 1, tagDoubleData, MPI_COMM_WORLD );

} else {

-тотакоепринимотзад0аетчи*/

 

 

/*Задачачто1

 

 

/*ж демсообщени

е ипомещапришданныевбуфмдшие*/р

 

MPI_Recv(

 

/* 1)

 

адресмассива,кудаdoubleData,

 

 

 

 

 

складыватьпринятое*/

 

фактическаяELEMS(длина doubleData ), /* 2)

 

 

 

 

приемногомассива

 

сообщаемMPI,что DOUBLE,

 

числеячеек*/

 

 

/* 3)

 

 

 

 

пришедшсообщение

 

 

 

 

состоизчиселтпа

 

откого:отзадачи0,0 */

 

'double' */

 

 

/* 4)

иестаким

ожидаемсообщенtagDoubleData,

/* 5)

 

 

 

идентификатором*/

 

описательобластиMPIсвяз, COMM_WORLD, /* 6)

 

 

 

 

черезкоторуюожидается

 

 

 

 

приходсообщения*/

 

&status );сюдабудетзаписанстатус/* 7)

 

/*Вычисляемфактическипринятоеколичданных*/ство

 

завершенияприема*/

 

 

 

 

MPI_Get_count(

/*

 

 

статусзавершения*/&status,

щаемMPI,чтопришедшее

 

MPI_сообDOUBLE,

/*

 

 

 

 

сообщенсостоитизчисел

 

 

 

 

типа'double' */

 

сюдабудетзаписанрезультат&count*/ ); /* /*Выводимфактическуюдлинупринятогонаэкран*/

printf("Received %d elems\n", count ); /*Аналогичнопринимсообщеданемтипанfloatымиие

Обратите* внимание:задача -приемникмеетвозможность принимать* сообщенияневтомпорядке,вкоторомони отправлялись* ,еслиэтсообщенияимеютразные * идентификаторы

*/

12

MPI_Recv( floatData, ELEMS( floatData ), MPI_FLOAT, 0, tagFloatData, MPI_COMM_WORLD, &status );

MPI_Get_count( &status, MPI_FLOAT, &count );

}

/*Обезадзавершаютчивыполнение*/ MPI_Finalize(); return 0;

}

Органприема/передачизданныхция межотдельнымипроцессамиу

Самыйпростойтипсвямеждузадачами:однаветвьвызывает

функциюпереданных, ачиругая -функциюприема.ВMPIэтовыглядит, например,так:

Задачапередает1 :

int buf[10];

MPI_Send( buf, 5, MPI_INT, 1, 0, MPI_COMM_WORLD );

Задачапринимает2 :

int buf[10]; MPI_Status status;

MPI_Recv( buf, 10, MPI_INT, 0, 0, MPI_COMM_WORLD, &status );

Прощевсегоэтоосуществляетсяфункциямиприема/передачи соосблокщений ировкой:

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

Параметры:

buf -адресначалабуферапосообщылки

ения;

 

count -числопередаваемыхэлементовсообщ

ении;

 

datatype -типпередаваемыхэлементов;

 

 

dest -но мерп оцесса -получателя;

 

 

msgtag -идентификаторсообщения;

 

 

comm -идентификаторгруппы.

 

Блокируюпосообылкаидентщаяияфикатором

 

 

msgtag,

состоящегоиз

countэлементовтипа

datatype,процессуномером

dest.Все

элементысообщениярасположеныдряд

вбуфере

buf.Значение count

можетбытьнулем,задаетсяневбайтах,вколичяче.Типествек

 

 

 

передаваемыхэлеме

нтов datatypeдолженуказыватьсяпомощью

 

 

предопределконсттип.Разрнтенныхредаватьшаетсясообщ

 

 

ение

самомусебе.

 

 

 

 

 

 

13

 

 

Блокировкагарантиру

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

 

параметровпослевозвратаизподпрограммы.Выбсп существленияоба

 

этойгарантии:копив рованиемежутбуфилипосредственнаячный

 

передачапроцессу

dest,остзаMPIСледуетяспециально. отметить,

что

возвратизподпрограммы

MPI_Sendнеознтогоаи,чсаетобщениеуже

 

переданопроцессу

dest,нитого,чтос общепокипроцниеулоссорный

MPI_Send.

элемент,накоторвыполняетсяпроцессм,выполнивший

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

Параметры:

 

 

 

 

 

 

buf -адресначалабуфераприемасообщениявыходной( пар

 

 

аметр);

count -максчиэлемемальсловпринимаемомсоооетов

 

бщении;

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

ения;

 

 

source -номерпроцесса

-отправителя;

 

 

msgtag -идентифпринсообщикатормаемого

ения;

 

 

comm -идентификаторгруппы;

 

 

 

 

status -параметрыпринятогосообщенияв( пархдной

 

аметр).

Приемсообщенидентияфикатором

 

msgtagотпроцесса

sourceс

блок.Чировкойсл

оэлементовпринимаемомсообщениинедолжно

 

 

 

превосходитьзначения

count.Есличислопринятыхэлементовменьше

 

 

значения count,тогарантируется,чтовбуфере

bufизменятсятолько

элементы,соответс

 

твующиеэлементампринятогосообщения.Еслинужно

 

 

 

узнать очислоноеэлементвоспользоватьсявсообщении,то жно

 

 

 

подпрограммой MPI_Probe.

 

 

 

 

Блокирогарантирует,чтопвслекаозвратаизподпрограммывсе

уфере buf.

 

 

элементысообщенияпррасположенынятыв

 

 

 

Еслипроцесспосылаетдвасообщениядругпр муцесс

 

 

 

уиобаэти

сообщенсоответствуютодниомуяжевызову

 

 

MPI_Recv,топервым

будетпринятосообщение,к торо

 

 

ылоотправленораньше.

 

 

Вкачественомерапроцесса

 

 

-отправителяможноуказать

 

 

предопределеннуюконстанту

 

MPI_ANY_SOURCE -признактого,что

 

подходитсообщениеотлюбпр.Вкачествегоцессаидентификатора

MPI_ANY_TAG -признак

принисообщемаеможноуказатьиянстантуго

 

того,чтоп содходит

 

бщенслюбымидентификатором.Послетакого

 

 

приемаданныхфактичномпроциеидентификаторскиесса

 

 

 

сообщения

записываютсяполя

MPI_SOURCE и MPI_TAGизструктуры

status.

Поле MPI_ERROR,какправило,проверне бязательноть

 

–обработчик

ошибок,устанавливаемыйпоMPIумолчанию,случаесбоязавершит

 

MPI_Recv.Таким образом,после

выпрограммыолнениедовозвратаиз

 

 

возвратаиз

MPI_Recvполе

status.MPI_ERRORможетбыравноолько0

 

 

(или,еслиуго

дно, MPI_SUCCESS).

 

 

 

 

 

 

 

14

 

 

Тип MPI_Statusнесодержитполя,вкотороезаписываласьбы

 

фактичедлинапришедшсообщкая.Длинуможноегония

знатьтак:

MPI_Status status;

 

int count;

 

 

MPI_Recv( ... , MPI_INT, ... , &status );

MPI_Get_count( &status, MPI_INT, &count );

/*теперь...countсодержитколичествопринятыхячеек*/

 

int MPI_Get_Count( MPI_Status *status,

 

 

MPI_Datatype datatype, int *count);

Параметры:

status -параметрыпринятогосообщения;

 

 

datatype -типэлементовпринятогосоо

бщения;

 

count -числоэлементовсообщениявыходной( пар

аметр).

Позначениюпараметра

statusданнаяподпрограммаопределяетчисло

 

 

ужепринятых(ослебращенияк

 

MPI_Recv)илипринимае

мыхпосле(

обращенияк

MPI_Probeили

MPI_IProbe)элементовсообщениятипа

 

datatype.

 

 

 

 

 

MPI_Recvи

Обратитевнимание,чтоаргумент

 

-описательтипау

 

MPI_Get_countдолженбытьодинаковым,иначе,зависимостиот

 

 

 

реализации:в

count вернетсяневерноезначениеил

ипроизойдетошибка

временивыполн

ения.

 

MPI_Recvполяструктуры

 

statusсодержат

Итак,повозвращениииз

 

 

инфопринятомсообщениимацию,функц я

 

 

MPI_Get_countвозвращает

количесфактическипринятводанн.Однакоимеетсяыхещеодна

 

 

 

 

 

функция,котпозрая

воляетузнхарактеристикахтьсообщениядотого,как

 

 

 

сообщениебудетпомещвприпользовательскиймныйнобуфер:

 

 

 

 

MPI_Probe.Заисключениемадразмераесапользовательскогобуфера,она

 

 

 

имееттакиежепараметры,каки

 

 

MPI_Recv.Онавозвращазаполненнуют

структуру MPI_Statusипосленееможновызвать

MPI_Get_count.Стандарт

MPIгара

нтирует,чтоследующийза

 

MPI_Probeвызов

MPI_Recvстемиже

параметрамиимеются( ввидуномерзадачи

 

 

-пере,идентатчификатор

сообщениякоммуникатор)поместитвбуферполь

 

кцией MPI_Probe.

зоваименнот ля

сообщен,котобылприфуноенято

 

 

 

 

int MPI_Probe( int source, int msgtag, MPI_Comm comm,

Параметры:

MPI_Status *status);

 

 

 

 

 

 

 

 

 

 

source -номерпроцесса

-отправителяили

MPI_ANY_SOURCE;

 

msgtag -идентификаторож

 

идаемогосообщенияили

 

MPI_ANY_TAG;

 

comm -идентификаторгруппы;

 

 

 

 

 

 

status -параметрыобнаруженсообщенияв( ого

 

ыходнойпараметр).

15

Получениеинформацииструктуреожидасообщениямого

 

блокировкой.Возвратаизподпрограммынепроиздотех,покайдет

 

сообщениесподходящимидентификаторомномеромпроцесса

-

отправителянебудетдоступнодляполучения.Атрибутыдоступного

 

сообщем жноия

пределитьобычнымобразомспомощьюпараметра

 

status.Следуетобратитьвнимание,чтоп дпрограммаопределяеттолько

 

фактприходасообщения,реальноегонеприним

ает.

MPI_Probeнужнавдвухслучаях:

 

Когзадача

-принзнемникаетдлиныранееожидаемогосообщения.

 

Пользовательбуферзаводитдинамическойп кийя

амяти:

MPI_Probe( MPI_ANY_SOURCE, tagMessageInt, MPI_COMM_WORLD, &status );

/* MPIвернетуправленProbeпослетогокаки*/мет /*данныевсистемныйбуфер*/

MPI_Get_count( &status, MPI_INT, &bufElems ); buf = malloc( sizeof(int) * bufElems ); MPI_Recv( buf, bufElems, MPI_INT, ...

/*даль шепараметрыу MPIтакиеже,каквRecvMPI_Probe */ );

/*останетсяMPIпростоскопироватьRecv*/ данные/*изсистемногобуферавпользовательский*/

Вместоэтог,конечно,можнопростозавестинаприемнойстороне

буферзаведомобольшой,чт

обывмесебятитьамоедл знное

возможныхсообщений,нотакойстильнеявляетоптимальным,есдлиная

сообщизмвслишкенширийяетсяпромких

еделах.

Когзадача

-приемниксобираетсообщенияотразныхотправителей

содержимымразныхтипов.Без

MPI_Probeпорядизвлечениясокобщений

буферпользователядолженбытьзаданмоменткомпиляции:

MPI_Recv(floatBuf, floatBufSize, MPI_FLOAT, MPI_ANY_SOURCE, tagFloatData, ... );

MPI_Recv(intBuf, intBufSize, MPI_INT, MPI_ANY_SOURCE, tagIntData, ... );

MPI_Recv(charBuf, charBufSize, MPI_CHAR, MPI_ANY_SOURCE,

tagCharData,

... );

 

Теперь,есливмоментвыпс лненияобщенсидентиефикатором

 

tagCharData

прираньшедетвухостальных, будетMPIвынужден

 

"законсе"егонавремявывироватьперолнения

 

выхд ухызовов

MPI_Recv.Эточр

еватонепроизводирасходамипамяти. ельными

MPI_Probe

позадатьволитпорядизвлечениясокобуферщенийпользователя

 

 

равнымпорядкуихпоступлениянапринимающуюсторону,делаяэтонев

 

 

моменепосредствекомпиляции,а

нновмоментвыполн

ения:

 

 

16

 

for( i=0; i<3; i++ ) { MPI_Probe(

MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&status ); switch( status.MPI_TAG ) {

case tagFloatData:

MPI_Recv( floatBuf, floatBufSize, MPI_FLOAT, ... ); break;

case tagIntData:

MPI_Recv( intBuf, intBufSize, MPI_INT, ... ); break;

case tagCharData:

MPI_Recv( charBuf, charBufSize, MPI_CHAR, ... ); break;

конец}switch/* */ }конец/*for */

Многотоздесьозначияаю

т,чтоп следниепарам4 у тра

MPI_Recv

тажекак, иупредшествующейим

MPI_Probe.

 

Использование MPI_Probeпродемонстрированоприм

ере2.

Пример2

/*

 

 

 

Приемсообщеннеизвестной* длины:

 

 

*

MPI_Probe

 

 

*

 

 

телейсразными

Приемсообще* отразотправинийых

 

*

идентификаторами

 

 

*ссодержимым(разныхтипов)произвольномпорядке:

 

(

*

MPIджокерыANY) SOURCE, MPI_ANY_TAG

*/

 

 

 

#include <mpi.h>

 

 

#include <stdio.h>

 

 

#include <stdlib.h>

 

 

#include <time.h>

 

 

/*Идентификаторысообще

ний*/

 

#define tagFloatData

1

 

#define tagLongData

2

 

Длина/*передавасообщможетбытьмыхний случайной*отдо1maxMessageElems

*/

#define maxMessageElems 100

17

int main(

int argc, char **argv )

{

size, rank, count, i, n, ok;

int

float

*floatPtr;

int

*longPtr;

char

*typeName;

MPI_Status status;

/*Инициализацсообщеношибкея целиком* перенесеныизпервогопримера

*/

MPI_Init( &argc, &argv ); MPI_Comm_size( MPI_COMM_WORLD, &size ); MPI_Comm_rank( MPI_COMM_WORLD, &rank );

/*пользовательдолжензапуститьровноТРИзадачи, иначеошибка*/

if( size != 3 ) { if( rank==0 )

printf("Error: 3 processes required ", "instead of %d\n", size );

MPI_Barrier( MPI_COMM_WORLD );

MPI_Abort( MPI_COMM_WORLD, MPI_ERR_OTHER ); return -1;

}

/*Каждаязадачаинициализируетгенераторслучайныхчисел*/

srand( ( rank + 1 ) * (unsigned )time(0) );

switch( rank ) {

case 0:

создаемсообщен/*случайнойдли*/ные

count = 1 + rand() % maxMessageElems; floatPtr = malloc( count * sizeof(float) ); for( i=0; i<count; i++ )

floatPtr[i] = (float)i;

/*Посылаемообщениевза

дачу2 */

 

MPI_Send( floatPtr, count, MPI_FLOAT,

 

2, tagFloatData, MPI_COMM_WORLD );

 

printf("%d. Send %d float items to process 2\n",

 

rank, count );

 

break;

case 1:

/* создаемсообщенслучайнойдли*/ные

count = 1 + rand() % maxMessageElems; longPtr = malloc( count * sizeof(long) );

18

for( i=0; i<count; i++ )

 

 

longPtr[i] = i;

 

 

Посылаемообщениевзадачу/*

 

 

 

2 */

 

MPI_Send( longPtr, count, MPI_LONG,

 

 

 

2, tagLongData, MPI_COMM_WORLD );

 

printf("%d. Send %d long items to process 2\n",

 

break;

rank, count );

 

 

 

 

 

 

за

case 2:

дачапринимает2 сообщениянеизвестной

 

/*

 

длины,используяMPI Probe*

 

 

 

 

*/

 

 

/*

 

 

Всегоожидаютсяfor(два*/ n=0; n<2; n++ )

 

 

сообщения*/{

 

/*

 

 

 

MPI_Probe(

 

/*

 

любойзадачи*/

MPI_ANYДжокSOURCE,:ждотемр

 

MPI_ANY_TAG,

 

/*

 

Джок:ждслюбымер

 

 

идентификатором*/

 

 

 

MPI_COMM_WORLD,

/* MPIвернетуправлениеProbe,когдасообщение

&status );

удет

уже*наприемнсторвслужебномбуферейне

 

*/

 

/*Проверяемидентификаторразмерпришедшегосообщения*/

if( status.MPI_TAG == tagFloatData )

{

 

MPI_Get_count( &status, MPI_FLOAT,

/*Принятоебудетразмещеновдинамическойпам

 

&count );

 

яти:

заказываем* нейбуферсоответствующейдл

ины

*/

floatPtr=malloc(count * sizeof(float));

 

 

MPI_Recv( floatPtr, count, MPI_FLOAT,

 

 

MPI_ANY_SOURCE, MPI_ANY_TAG,

/* простоMPIскопируетужеRecvпринятыеда

 

MPI_COMM_WORLD, &status );

 

нные

из*системногобуфера

 

впользовательский

*/

/*

 

Проверяемпринятое*/

 

 

for( ok=1, i=0; i<count; i++ )

 

 

if( floatPtr[i] != (float)i )

ok = 0; typeName = "float";

}

else if( status.MPI_TAG == tagLongData )

{

19

Дейст,аналогичныевышеописаннымия/* */

MPI_Get_count(&status,MPI_LONG, &count); longPtr = malloc(count * sizeof(long));

MPI_Recv( longPtr, count, MPI_LONG, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status );

for( ok=1, i=0; i<count; i++ ) if( longPtr[i] != i )

ok = 0; typeName = "long";

}

/*Докладывазавершенииприема*/

printf( "%d. %d %s items are received ", "from %d : %s\n", rank, count, typeName,status.MPI_SOURCE,

(ok ? "OK" : "FAILED") );

} /* for(n) */ break;

} /* switch(rank) */

Завершениеработы/**/ MPI_Finalize(); return 0;

}

 

 

Вэтомприспользуюмерепараметрыся

MPI_ANY_SOURCEдля

номеразадачи

-отправителяпринимай(" коуго")дно

MPI_ANY_TAG

дляидентификатораполучаемогосообщепри("чтугоднони")майя.

 

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

 

вызовом MPI_Recvможетбытьзахваченосообщение,котд лжноро

принимвдругойчасз тдачиься

 

-получателя.

Еслилогикапрограммыдостаточносложна,использоватькие

MPI_Probeи MPI_Iprobe,

параметры-джокерыжелательнотольковфункциях

чтобыпередфактичприузнатьескимколичествопданных

 

поступившемсообщенахудой( конец,можноиипри,инезнаимать

я

количества -былприемныйбуфердостаточновмес, тидлятельнымп

MPI_Recvнадоуказыватьявно

–аонможетбытьразнымвсообщениях

разнымиидентифик

аторами).

 

Достоинстводжокер:прихсообщенияизвлекаютсядящиепом ре

MPI_Recvснужнымиидент фикаторами

поступления,анем

еревызова

задач/сообще.Этоэкопамятьномитувеличиваетийскоростьраб

оты.

 

 

20

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