Библиотека MPI [Электронный ресурс] (90
..pdf
/*Идентификаторысообщений*/
#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 |
